Skip to content

const enum lookups reject values that appear on the wire: RouterAlert(0) is RFC 2113's only defined value #492

Description

@JarryShaw

Found during the post-wave-1 consistency sweep (docs/source/pep.rst). Three lookup defects in pcapkit/const/**, all reproduced by me on 5959a437c. They share a subsystem and a one-PR fix, so they are filed together, but their severities are genuinely different and are labelled honestly below rather than levelled up.

The sweep behind them: I instantiated every IntEnum under pcapkit.const with value 0 — 111 classes — and separately checked every _missing_ for the @classmethod decorator.

1. HIGH — RouterAlert(0) rejects RFC 2113's only defined value

pcapkit/const/ipv4/router_alert.py. RFC 2113 §2.1 defines the Router Alert value field as:

Value:  A two octet code with the following values:
  0 - Router shall examine packet
  1-65535 - Reserved

Value 0 is the only thing RFC 2113 defines, and it is what IGMP, RSVP and MLD put on the wire. The enum does not have it. Members start at Aggregated_Reservation_Nesting_Level_0 = 1 (the RFC 3175 registry, correctly), and _missing_ at :236-251 covers only 66..65502 and 65503..65534. So 0 falls through to super()._missing_ and raises:

RouterAlert(0)     -> ValueError: 0 is not a valid RouterAlert
RouterAlert(1)     -> <RouterAlert.Aggregated_Reservation_Nesting_Level_0: 1>
RouterAlert(32)    -> <RouterAlert.Aggregated_Reservation_Nesting_Level_31: 32>
RouterAlert(65535) -> <RouterAlert.Reserved_65535: 65535>

A standards-compliant packet therefore fails to parse. Built one — IPv4 carrying a Router Alert option (type 148, length 4, value 0) ahead of an IGMP payload:

wire:   4600001c00000000000200007f000001000000009404000011000000
parse -> ValueError: 0 is not a valid RouterAlert

Note the exception class: ValueError from aenum, not a pcapkit.utilities.exceptions error, so it escapes the library's own exception hierarchy.

The gap is in the vendor registry generation: IANA's IPv4 Router Alert Option Values registry lists 0 (RFC 2113) alongside 1-31 (RFC 3175), and only the latter came through.

2. MEDIUM — Socket(0) rejects a real wire value, and it is IPX's own default

pcapkit/const/ipx/socket.py. Every unregistered-value bucket in Socket._missing_ (:79-103) starts at 0x0001 or higher — 0x0001-0x0BB8, 0x0020-0x003F, 0x0BB9-0xFFFF, 0x4000-0x4FFF, 0x8000-0xFFFF — so 0x0000, an ordinary "unspecified socket", reaches aenum's default _missing_ and raises.

Socket(0x0000) -> ValueError: 0 is not a valid Socket
Socket(0x0001) -> <Socket.Routing_Information_Packet: 1>
Socket(0x0451) -> <Socket.NetWare_Core_Protocol: 1105>

0x0000 is also IPX's own class default for dst/src, so the class cannot construct itself with its own documented defaults:

bytes(IPX(payload=b'\xde\xad\xbe\xef'))
ValueError: 0 is not a valid Socket
    at pcapkit/const/ipx/socket.py:74 in get

Rated MEDIUM rather than HIGH for one measured reason: through the full pipeline the beholder wrapper catches it and degrades the layer to Raw, so a capture containing such a frame comes back as Ethernet:Novell_Inc_0x8137 with 'error': '0 is not a valid Socket' instead of crashing. It silently loses the IPX parse rather than failing loudly. With a registered socket (0x0451) the identical packet parses fine, which isolates the cause to the missing bucket.

3. LOW, and latent rather than live — two _missing_ methods lack @classmethod

pcapkit/const/ftp/return_code.py:44 (ResponseKind) and :66 (GroupingInformation). aenum invokes cls._missing_(value); without the decorator the lone argument binds to the cls parameter and value goes unfilled:

ResponseKind(0)  -> TypeError: ResponseKind._missing_() missing 1 required positional argument: 'value'
ResponseKind(7)  -> TypeError: ... (same)
ResponseKind(99) -> TypeError: ... (same)

So every unknown value fails, not merely 0, and with a TypeError that the _missing_ body was written to avoid. The sweep makes the intent unambiguous:

_missing_ WITH    @classmethod : 107
_missing_ WITHOUT @classmethod :   2   <- both in ftp/return_code.py

Why LOW, stated plainly: these two const classes are imported by no runtime code. grep across pcapkit/, tests/ and examples/ finds references only in pcapkit/const/ftp/return_code.py itself and in the separate codegen copy pcapkit/vendor/ftp/return_code.py, which carries the identical missing decorator at :73/:95 and does call them at :124-125:

obj.kind  = ResponseKind(int(code[0]))
obj.group = GroupingInformation(int(code[1]))

That path is reachable only during vendor regeneration, and only for a reply code whose digits fall outside the defined members. Since codegen currently succeeds, no code in today's IANA registry triggers it. It is a one-line fix in two (really four) places and worth doing, but I am not claiming it breaks anything today.

The other six, checked and cleared

Six further enums also reject 0, and for all six that is correct — 0 is not a valid protocol value, so no change is wanted:

ftp.return_code.ReturnCode and http.status_code.StatusCode (both are 3-digit codes, 100-599), ftp.command.ConformanceRequirement, sctp.cause_code.CauseCode and sctp.parameter.Parameter (registries that begin at 1, with 0 reserved), and mh.mn_id_subtype.MNIDSubtype (subtypes begin at 1 with NAI — the enum behind #469/#481).

So of 111 enums swept: 102 accept 0, 6 rightly reject it, 2 are defects (1 and 2 above), and 1 is the broken-signature case.

Suggested shape

For 1 and 2 the fix is a registry-accuracy one — add the missing 0 member (RouterAlert gets RFC 2113's "Router shall examine packet"; Socket gets an unspecified-socket entry or a _missing_ bucket reaching down to 0x0000) in both pcapkit/const/ and the matching pcapkit/vendor/ generator, or the next regeneration reverts it. For 3, add @classmethod in both files.

A test sweeping every pcapkit.const IntEnum for "does Enum(0) behave as the registry says it should" would pin all of this permanently; the probe above is ~20 lines and already does the enumeration.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssues reporting a defect (set by the bug report template; a default, not an assessment)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions