Repository navigation
Commit ef889e9
committed
feat(internet): implement ICMP and ICMPv6, sharing a thin dispatch base
Closes #2436. ICMP (RFC 792) and ICMPv6 (RFC 4443) had no implementation at
all -- Internet.__proto__ had no entry for either protocol number, so a
packet carrying one fell back to Raw with only the protocol number as a
hint.
- Add ICMP_Family between Internet and ICMP/ICMPv6, following the IP/IPsec
precedent: nothing but id(). The base's own name is "_Family"-suffixed
(matching IPv6_Route, IPv6_Ext) because, unlike IP/IPsec, the family's
generic name collides with one of its own members (ICMP).
- ICMP: Echo/Echo Reply, Destination Unreachable, Time Exceeded, Parameter
Problem, plus a raw fallback for any other type. Own type registry
(__type__, register_type), own schema. Both classes construct with no
arguments (default body is the minimum valid length) and compute/verify
their own checksum via a new shared pcapkit.utilities.checksum.inet_checksum
(RFC 1071): ICMP sums the message alone, ICMPv6 additionally sums the IPv6
pseudo-header (RFC 2460 Section 8.1) when the enclosing IPv6 addresses are
known; a bad checksum warns rather than raises.
- ICMPv6: Destination Unreachable, Packet Too Big, Time Exceeded, Parameter
Problem, Echo Request/Reply, plus the same raw fallback. Neighbor Discovery
(RFC 4861) and MLD (RFC 9777, which obsoletes RFC 3810) are explicitly out
of scope and not yet tracked by an issue of their own.
- New const/vendor registries: icmp.type and its three code registries, and
the icmpv6 counterparts, as real IANA CSV crawlers (not hand-typed data),
so a vendor-cron run picks up registry changes the way every other
protocol's constants do.
- Wire ICMP/IPv6_ICMP into Internet.__proto__, and delete the two 0-byte
NotImplemented stubs.
- docs/source/contributing/pep.rst: correct the ICMP/ICMPv6 follow-up prose
(MLD's RFC citation, the Neighbor Discovery/NDP cross-reference, and a
stale "wave 2 deliverable" claim about MLD elsewhere in the same file).
- Update every registry-shape test the new entries touch: the const enum
sweep-size pins, the dispatch registry's case count and PINNED_TARGETS,
the package re-export and public-API surface checks, the vendor
reachability and missing-body sweeps, and the handful of runtime/
integration tests whose protochain string literally said "IPv6_ICMP"
where it now says "ICMPv6". Also moved an unrelated test fixture off
IGMP's protocol number, which #2439 is about to register.
- Cross-review round: checksum verification used to warn on genuinely valid
traffic -- a first IP/IPv6 fragment, a short-snaplen capture, and an IPv6
Routing Header with Segments Left set -- by summing a slice of the message
against a checksum computed over the whole thing; all three now skip
verification instead of warning. ICMPv6 reached over IPv4 (protocol 58)
used to crash the layer to Raw (IPv4Address fed to IPv6Address); it now
skips verification the same way. inet_checksum's end-around-carry fold was
hardcoded to two rounds, wrong for inputs around 131078+ octets; it now
loops until no carry remains, per RFC 1071. Three docstrings claiming the
checksum is never verified/computed are corrected, and icmpv6.py's MLD
citation is brought in line with pep.rst's own RFC 9777 correction.
- Cross-review round 3: a Mobile IPv6 node (RFC 6275 Section 11.3.1)
checksums against its home address, not the wire-visible care-of address
in ipv6.src; IPv6._decode_next_layer now threads the Destination Options
Home Address option through __packet__['home_address'] for ICMPv6.read to
use in its place. A malformed Routing header (wrong Hdr Ext Len) falls
back to the generic IPv6_Ext parser, whose info has no seg_left key;
info.get('seg_left', 0) silently read that as zero instead of "unknown",
letting a non-zero Segments Left through unflagged. Segments Left is now
read from the header's raw 4th octet whenever the structured parse did
not leave it behind.
- Cross-review round 6: two more instances of the same root cause, each
found by a payload that reaches ICMP/ICMPv6 through a path the earlier
flags never covered. (A) IP reassembly's Deferred(self.protocol.analyze,
...) threaded no packet context at all, so a reasm_strict=False datagram
-- a zero-filled hole or a truncated prefix -- reached ICMP/ICMPv6 looking
exactly like a complete message, including through a tunnel (4-in-4,
6-in-6) whose inner layer's own fresh parse has no way to know the outer
datagram might be incomplete. (B) A malformed sibling option (e.g. a
wrong-length Tunnel Encapsulation Limit) knocks IPv6_Opts into its generic
IPv6_Ext fallback, whose info carries no options at all, silently losing a
genuine Home Address option the same way round 3's Routing header bug
lost Segments Left.
Rather than add a sixth ad-hoc __packet__ key, the five this grew across
five rounds (mf, route_seg_left, home_address, and two unnamed checks) are
replaced by one ChecksumContext (pcapkit/protocols/internet/icmp_family.py):
`incomplete` (sticky across tunnel nesting -- a fragment or a reassembly
dispatch), `misrouted` (scoped to one layer -- a Routing header; keeping
it separate from `incomplete` is what a regression caught mid-round --
conflating the two let an outer Routing header silence a genuinely corrupt
inner checksum), and `src`/`dst` (never inherited by a nested layer, and
may name ADDRESS_UNKNOWN when an address override was due but could not
be recovered, as opposed to "no override is due"). ICMP and ICMPv6 now
call the same verifiable_for_icmp()/verifiable_for_icmpv6() rather than
reading ad-hoc flags apiece; fail-closed by construction and documented in
one docstring instead of five scattered ones.
Deferred gained an optional packet= parameter, purely additive (every
existing call site omits it and sees no change) and still picklable under
every protocol (#2423/#2425/#2435), so IP.submit can seed every reassembly
dispatch already `incomplete`, unconditionally: that second parse never
verifies a checksum of its own, complete datagram or not, since whichever
fragments needed checking were already checked, with real context, on
their own first pass through the frame. IPv6._home_address recovers a
Destination Options Home Address by walking the raw option TLVs directly
when the dedicated parser's fallback info has none -- the same move round
3 made for a Routing header's Segments Left -- and reports ADDRESS_UNKNOWN
rather than guessing "no Home Address here" when the walk cannot complete
cleanly.
- Cross-review round 7: round 6's own ChecksumContext fix had three further
gaps, plus one unrelated pickling regression it introduced.
(A) Eight extension-header parsers (AH, IPv6_Opts, IPv6_Route, HOPOPT,
IPv6_Frag, MH, HIP, IPv6_Ext) dispatch to their own next layer with no
packet= of their own whenever they are the outermost protocol rather than
a header IPv6._decode_next_layer is itself walking, silently dropping
ChecksumContext and leaving ICMP/ICMPv6 with a fresh, never-populated one
-- reproduced by an AH-protected first IP fragment losing the fragment's
own `incomplete` flag and warning on a checksum that only ever covered
part of the message. Fixed structurally rather than at each of the eight
sites: a new ambient `_checksum_context` contextvars.ContextVar (the same
pattern `_frame_depth` already uses) is set by IPv4.read, IPv6.read and
Deferred.__call__ around their own dispatch, and
ChecksumContext.from_packet falls back to it whenever __packet__ carries
no context of its own -- so any intervening layer, these eight or one
written later with no knowledge of this feature at all, is covered
without a fix of its own.
(B) IP.submit seeded every reassembly dispatch's ChecksumContext as
unconditionally `incomplete`, which went too far: a genuinely corrupt
checksum spread across multiple fragments became unreachable everywhere,
never checked on any non-first fragment's own pass (there isn't one) and
no longer checked on the reassembled pass either, complete or not. It is
now `incomplete` only while reassembly itself is still incomplete, or
when at most one fragment contributed (already checked on its own direct
first pass) -- a multi-fragment datagram that reassembled in whole is
finally verified, on the only pass that was ever going to see the whole
thing.
(C) IPv6._home_address's raw-byte-scan fallback treated a Home Address
option of any length other than 16 the same as no option at all, letting
ICMPv6 verify against the wire source as if Mobile IPv6 were not in
play; a present-but-wrong-length option now reports ADDRESS_UNKNOWN, same
as the framing breaks it already handled.
(D) Deferred.__call__ read self.packet directly, so a Deferred pickled
before round 6's packet= parameter existed raised AttributeError on
call despite that parameter's "purely additive" versionchanged note; it
now reads packet through getattr with a None default.
- Cross-review round 10: round 9 confirmed the design closes every
reachable path; mutation testing found Deferred.__call__'s own
_checksum_context.set() -- unlike the equivalent set() in IPv4.read and
IPv6.read -- had no regression test. Pinned with a new extract()-driven
case: AH riding directly on IPv4 (no IPv6) wrapping a corrupt-checksum
ICMP, reassembled as a single fragment -- the one reachable shape where
AH's own _decode_next_layer drops packet= on its way to ICMP, making
the Deferred's ambient-context seed the ICMP layer's only source of
context. Confirmed to actually depend on the fix: temporarily removing
the .set() call makes the new test catch the corrupt checksum twice
instead of once.
Build: the round's own command -- coverage run -m pytest over the ICMP/
ICMPv6/checksum unit tests, tests/foundation/reassembly, tests/const,
tests/vendor and tests/integration -- passes clean (915 passed, 27918
subtests, zero "invalid checksum" warnings on genuinely valid traffic, and a
genuinely corrupt reassembled checksum now warns). The full
tests/protocols/internet (623 passed, 12530 subtests) and tests/foundation
(580 passed, 17 skipped, 1858 subtests) directories, covering every prior
round's own regression test by name, pass clean as well. Round 10's own
command -- coverage run -m pytest over tests/foundation/reassembly, the
ICMP/ICMPv6 roundtrip suites and the checksum unit tests -- passes clean
as well (244 passed, 910 subtests).1 parent e1efee7 commit ef889e9
82 files changed
Lines changed: 7451 additions & 86 deletions
File tree
- docs/source
- contributing
- conventions
- pcapkit
- const
- protocols/internet
- utilities
- vendor
- examples/generators
- pcapkit
- const
- icmpv6
- icmp
- corekit
- foundation/reassembly
- data
- protocols
- data
- internet
- internet
- NotImplemented
- schema
- internet
- utilities
- vendor
- icmpv6
- icmp
- tests
- const
- foundation/reassembly
- integration
- protocols
- internet
- link
- misc/pcap
- utilities
- vendor
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
54 | 65 | | |
55 | 66 | | |
56 | 67 | | |
| |||
349 | 360 | | |
350 | 361 | | |
351 | 362 | | |
352 | | - | |
353 | | - | |
354 | | - | |
355 | | - | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
356 | 367 | | |
357 | 368 | | |
358 | 369 | | |
| |||
1071 | 1082 | | |
1072 | 1083 | | |
1073 | 1084 | | |
1074 | | - | |
1075 | | - | |
1076 | | - | |
1077 | | - | |
1078 | | - | |
1079 | | - | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
1080 | 1091 | | |
1081 | 1092 | | |
1082 | 1093 | | |
| |||
1104 | 1115 | | |
1105 | 1116 | | |
1106 | 1117 | | |
1107 | | - | |
1108 | | - | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
1109 | 1123 | | |
1110 | 1124 | | |
1111 | 1125 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| 83 | + | |
| 84 | + | |
83 | 85 | | |
84 | 86 | | |
85 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
0 commit comments