Skip to content

fix(corekit): IPv4InterfaceField reads hostmask-shaped masks as hostmasks and repacks a different mask #1294

Closed

Description

@JarryShaw

Describe the bug
IPv4InterfaceField.post_process builds the interface with ipaddress.ip_interface(f"{ip}/{mask}") (pcapkit/corekit/fields/ipaddress.py:432), and the stdlib treats a mask such as 0.0.0.255 as a hostmask. The octets are then re-derived as a netmask on pack. Affects pcapng IF_IPv4AddrOption.

To Reproduce

f = IPv4InterfaceField()
v = f.unpack(bytes.fromhex("0a000001000000ff"), {})   # 10.0.0.1/24
f.pack(v, {}).hex()   # "0a000001ffffff00"
# 0a0000017fffffff -> 10.0.0.1/1 -> 0a00000180000000

Repro: /tmp/rtissues/corekit-repro.py (check D8) on origin/main 11ae68e.

Expected behavior
The four mask octets round-trip as read (keep the netmask as given, or reject a non-contiguous mask instead of reinterpreting it).

Found by the corekit round-trip audit (#1202). The repro was re-checked by an independent verifier, and I re-ran it on main at 11ae68ebf.

Activity

  1. added
    fixPull requests that fix a defect (fix: subject prefix)
    pendingAccepted and unstarted - nobody is on it and nothing blocks it
    on Oct 7, 2026
  2. JarryShaw commented on Oct 7, 2026

    @JarryShaw
    OwnerAuthor

    A worker is on it now.

  3. added
    wipWork in flight - a covering PR is open or an agent is actively on it
    and removed
    pendingAccepted and unstarted - nobody is on it and nothing blocks it
    on Oct 7, 2026
  4. JarryShaw commented on Oct 7, 2026

    @JarryShaw
    OwnerAuthor

    Decision needed on a string-parsing side effect of #1346. The PR keeps a non-contiguous wire mask, so 0a000001000000ff parses to 10.0.0.1/0.0.0.255. To make that string pack back byte-exactly, it now reads a dotted mask literally whenever the mask is not a contiguous netmask. The stdlib reads the same string as a hostmask:

    input string main packs #1346 packs stdlib with_netmask
    10.0.0.1/0.0.0.255 …ffffff00 (/24) …000000ff (literal) /255.255.255.0
    10.0.0.1/255.0.255.0 FieldValueError …ff00ff00 ValueError
    10.0.0.1/24, /255.255.255.0 …ffffff00 …ffffff00 /255.255.255.0

    Options:

    • (1) Keep fix(corekit): IPv4InterfaceField keeps non-netmask masks; OrderedMultiDict deepcopy keeps order #1346 as it is. The field is a netmask on the wire, so a dotted value is the mask itself. That gives str()→pack round trips for every wire value. The cost is that a user who passes the stdlib's hostmask spelling of /24 now gets a different mask.
    • (2) Keep the stdlib hostmask reading for string input, and give non-contiguous values an unambiguous str() form (e.g. a raw hex mask). Wire round trips still hold; string round trips hold only through that form.

    I recommend (1): it matches the every-round-trip ruling and the field's wire meaning. It is a breaking change, and the PR body states it.

  5. added
    needs: decisionWaiting on the maintainer to decide — not blocked by other work
    on Oct 7, 2026
  6. JarryShaw commented on Oct 7, 2026

    @JarryShaw
    OwnerAuthor

    (1) sounds right but if stdlib's hostmask will be parsed different, can we either drop stdlib or fix that properly (prefer the latter)?

  7. JarryShaw commented on Oct 7, 2026

    @JarryShaw
    OwnerAuthor

    Ruling recorded: option (1), keeping the wire mask literal, but without diverging from the stdlib wherever the stdlib already defines a meaning. Fix the ambiguity properly rather than dropping the stdlib.

    Concrete shape, now going to #1346:

    • String input keeps the stdlib's meaning wherever the stdlib accepts it. A contiguous netmask, /prefixlen, and a hostmask such as 10.0.0.1/0.0.0.255 (which means /24) all parse exactly as on main.
    • Only masks the stdlib rejects are read literally. Non-contiguous masks such as 255.0.255.0 fall into this group. They raise on main today, so no existing input changes meaning.
    • str() of a raw-mask value never produces a string the stdlib would read differently. A wire mask that is hostmask-shaped (0.0.0.255 on the wire) renders in an unambiguous form instead of dotted quad, and pre_process accepts that form back. So pack(str(unpack(wire))) == wire still holds for every mask, and no stdlib-legal string changes meaning.

    If this reading is off, say so on #1346 before it lands.

  8. removed
    needs: decisionWaiting on the maintainer to decide — not blocked by other work
    on Oct 7, 2026
  9. removed
    wipWork in flight - a covering PR is open or an agent is actively on it
    on Oct 7, 2026
  10. added this to the PyPCAPKit v1.5 milestone on Oct 25, 2026
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

    fixPull requests that fix a defect (fix: subject prefix)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions