Skip to content

feat(link): add Netlink dissector for ARPHRD_NETLINK on LINUX_SLL/SLL2 - #2507

Open
JarryShaw wants to merge 1 commit into
mainfrom
feat/2500-netlink-dissector
Open

JarryShaw wants to merge 1 commit into
mainfrom
feat/2500-netlink-dissector

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Part of #2460.

What

LinuxSLL/LinuxSLL2 fell back to Raw for the NETLINK arphrd_type
(824). This adds pcapkit.protocols.link.netlink.Netlink, dissecting the
fixed 16-octet nlmsghdr framing (nlmsg_len/nlmsg_type/nlmsg_flags/
nlmsg_seq/nlmsg_pid), and wires LinuxSLL's shared _import_next_layer
to dispatch ARPHRD_NETLINK there instead.

Scope

In scope: the generic nlmsghdr framing itself. Researched from the
Linux kernel's own netlink(7) man page and RFC 3549 §2.3.2 (both quoted in
the module docstring).

Out of scope, documented explicitly:

  • Per-family message payloads (routing table entries, NETLINK_GENERIC,
    etc.) — a large surface of its own, numerous and family-specific. Every
    message's payload is kept as Raw, unconditionally.
  • Multiple nlmsghdr messages packed into one capture frame — netlink
    reads commonly carry more than one message back to back. This first cut
    dissects only the first message's header; everything after it (that
    message's own payload, and any sibling messages) is kept together as the
    undissected Raw payload. Tracked as future work on Dissectors for non-ethertype ARPHRD types on LINUX_SLL/SLL2 (NETLINK/IPGRE/IP6GRE/802.11 radiotap/FRAD) #2460.

Byte order is ambiguous — Netlink is kernel/userspace IPC, not wire
format, and neither netlink(7), RFC 3549, nor tcpdump.org's
LINKTYPE_NETLINK page record a byte-order marker for it. nlmsg_byteorder()
resolves it with the same heuristic Wireshark's own netlink dissector uses
(packet-netlink.c): decode the length field both ways and keep whichever
reading comes closer to the octets actually available. This mirrors the
existing precedent in this codebase —
pcapkit.protocols.link.loopback.family_byteorder solves the identical
problem for LINKTYPE_NULL.

Registration

Registered in all 7 places Loopback/VLAN are (plus an 8th, pcapkit/all.py,
caught by test_all_exports_unit.py on the way):
pcapkit/protocols/link/__init__.py, pcapkit/__init__.py,
pcapkit/protocols/__init__.py, pcapkit/protocols/data/__init__.py,
pcapkit/protocols/data/link/__init__.py, pcapkit/protocols/schema/__init__.py,
pcapkit/protocols/schema/link/__init__.py, pcapkit/all.py.

Tests

  • tests/protocols/link/test_netlink_unit.py (new): 14 tests / 49 subtests.
  • Updated test_linux_sll1_unit.py/test_linux_sll2_unit.py's now-stale
    "NETLINK stays Raw" assertions, added an explicit dispatch test to each.
  • Added a short-read case to test_short_read_roundtrip_unit.py.
  • tests/project/'s two project-wide sweeps caught two real gaps: a missing
    pcapkit/all.py export, and this module's 16/65535 literals colliding
    with unrelated homed constants in test_rfc_constants_one_home_1739_unit.py's
    sweep — both fixed with the same precedent linux_sll1.py already uses.

Counts: tests/protocols/link/ 185 passed / 1076 subtests (incl. the
two project sweeps above); tests/protocols/test_short_read_roundtrip_unit.py
9 passed / 1374 subtests; tests/project/ full run 2132 passed, 80 skipped,
10555 subtests, 0 failed. pylint 10.00/10, mypy clean on all touched files.

Changelog & docs

Entry in docs/source/changelog/1.6.0.rst, CHANGELOG.md regenerated
(util/changelog_md.py --check clean), new docs page
docs/source/pcapkit/protocols/link/netlink.rst indexed in the link-layer
toctree.

Part of #2460.

- Add Netlink (nlmsghdr 16-octet framing: nlmsg_len/type/flags/seq/pid),
  with schema/data modules, registered in all 7 places LinuxSLL/VLAN use
  (plus pcapkit/all.py, an 8th aggregator caught by test_all_exports_unit.py).
- Byte order is ambiguous for Netlink (kernel/userspace IPC, not wire
  format); resolved by nlmsg_byteorder(), the same heuristic Wireshark's
  own netlink dissector uses.
- Wire LinuxSLL/LinuxSLL2's _import_next_layer to dispatch ARPHRD_NETLINK
  to Netlink instead of Raw; update the two LinuxSLL test modules'
  now-stale Raw assertions.
- Per-family message payloads and multiple nlmsghdr messages packed into
  one capture are out of scope for this first cut; every payload is kept
  as Raw. IPGRE/IP6GRE and IEEE80211_RADIOTAP remain Raw.
- Add tests/protocols/link/test_netlink_unit.py, a short-read case, and
  exemptions in test_rfc_constants_one_home_1739_unit.py for the 16/65535
  literals this module shares with unrelated homed constants.
- Changelog entry in 1.6.0.rst, regenerated CHANGELOG.md, and a docs page.

Build: pylint 10.00/10, mypy clean. Tests: tests/protocols/link/ (176
passed, 2301 subtests), tests/protocols/test_short_read_roundtrip_unit.py
(9 passed, 1374 subtests), tests/project/ (2132 passed, 80 skipped,
10555 subtests, 0 failed) -- all green, confirmed by a full re-run after
the two project-wide fixes above.
@JarryShaw JarryShaw added feat Pull requests that add a new capability (feat: subject prefix) review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Oct 31, 2026
@JarryShaw JarryShaw added review: running A cross-review is in flight against the current head - no verdict yet and removed review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Oct 31, 2026
@JarryShaw JarryShaw moved this to In review in PyPCAPKit Oct 31, 2026
@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 31, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Cross-review (Sonnet) at 870e7e9e0: GOOD TO GO. The byte-order heuristic was independently confirmed against Wireshark's own packet-netlink.c (dissect_netlink_cooked) -- the two implementations use the exact same test. RFC 3549 §2.3.2 and netlink(7) both confirmed to match the docstring's claims. Round-tripped a real 2-message frame in both byte orders, byte-exact. Registration confirmed in all 8 places. Full suite reproduced with 0 failures (subtest counts differ slightly from the author's self-report, likely a scope difference in which sweeps were included, but outcomes match exactly). One non-blocking nit: a docstring sentence slightly overstates what RFC 3549 omits. Ready for merge -- your call.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage: 94.47% (unit tier, Python 3.15, 870e7e9e0, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 4 20 2 95.16%
pcapkit/const 19738 133 2642 120 98.85%
pcapkit/corekit 2605 74 818 37 96.52%
pcapkit/dumpkit 281 3 100 3 98.43%
pcapkit/foundation 3183 27 1110 31 98.65%
pcapkit/interface 138 4 52 3 96.32%
pcapkit/protocols 18557 200 4886 185 98.35%
pcapkit/toolkit 924 36 334 10 95.55%
pcapkit/utilities 502 7 144 5 98.14%
pcapkit/vendor 4510 1888 1034 169 54.89%

Per-file detail: the coverage-html artifact of this run.

This branch has not been deployed

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

Labels

feat Pull requests that add a new capability (feat: subject prefix) review: good-to-go Cross-review at the current head says ready; CI state is separate

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

1 participant