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.

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