Repository navigation
fix(toolkit): dpkt hands IPv4 reassembly what the default engine does (#1615, #1616) - #1622
Conversation
…#1615, #1616) - #1615: ipv4_reassembly took the network layer only, so IPv4 carried in IPv6 gave no input. The new _first_ipv4 walks IPv6 (behind extension headers, or 6in6) to the first IPv4 layer, bounds its octets by each Payload Length above, and stops where the default engine dissects no IPv4: a later fragment, more than 32 extension headers, or a zero Payload Length with no Jumbo Payload option. - #1616: the input was cut at the Total Length, truncating a header longer than it and dropping a padded fragment's trailer. It is now the IHL's octets and the rest, as the default engine hands them; the reassembler applies the Total Length itself. New tests/toolkit/test_dpkt_ipv4_reassembly_input_1615_1616_unit.py compares dpkt's inputs with the default engine's over 21 cases; it fails without the fix, and each guard is load-bearing.
|
Coverage: 89.72% (unit tier, Python 3.14,
Per-file detail: the |
|
Verdict: GOOD TO GO at Sonnet cross-review. It compared every IPv4 reassembly input field and the datagrams, default against dpkt, on 49 chain cases: all 49 match on the PR, while 28 differ on the base. Link layers and 42 truncation cut points also match, and the nested Payload-Length extent was re-derived by hand: 78, then 30, so payload 10 as measured. Non-blocking: two of the three function-local imports could move to module top, which the review checked creates no cycle. I narrowed the body's "any one guard" claim to the three guards the mutants confirm. Out of scope and filed as #1626: dpkt and scapy drop TCP and IPv4 behind some IPv6 chains, such as a Fragment header followed by AH, or Mobility. |
…test - The Total Length 10 case and the accepted 4in6 case set DF so that no engine made an IPv4 input, because dpkt cut the header at the Total Length (#1616) and found no IPv4 in IPv6 (#1615). #1622 fixed both, so DF now only hid two inputs from the comparison. - Both cases drop DF and expect an IPv4 input (frames 17 and 40; 15 -> 17 inputs compared); the unused df parameter of ipv4() goes with it. With #1622's pcapkit/ change reverted the test now fails for dpkt (frame 17 header/payload differ, frame 40 missing); before, it passed. 4 passed on 3.14 and 3.11.
Third-party engines are best effort (#1687), so the dpkt code that exists only to match the default engine comes out: - revert #1662, #1655, #1624, #1622, #1614 and the dpkt hunks of #1605: the octet walk and its link-type stash, IPv6 in IPv4 and IPv4 in IPv6, refusing MPLS and ISL, the tunnel and extension-header walks, the IPv4 header checks and the layer bounds - keep #1690's timestamps, #1594's parser choice, #1621 and #1653 - re-land as guards: a layer dpkt's own lengths misplace (#1646) is re-serialised, not refused with ProtocolError; a Total Length below the IHL keeps the header whole (#1616), and a header the capture cuts is no input; packet2dict and packet2chain take a str payload and a raw frame - delete the modules #1662 and #1655 added; the 1615/1616 module keeps only what its #1616 tests use, as no scapy test imports it now; dpkt's comparisons with the default engine assert constants or the capture's octets, or are dropped - drop pypcapfile's VLAN_TAGS, VLAN_TAG_LEN and IPV6_EXTENSION_HEADERS, which only dpkt imported, and the 1668 module's _Agreement base, which nothing subclasses now - the docs drop the depth claims and name what dpkt leaves out silently The dpkt modules, the shared modules that run dpkt, tests/foundation/engines and the project guards pass on 3.14 and 3.11 (dpkt 1.9.8, pypcapfile 0.12.0).
Third-party engines are best effort (#1687), so the dpkt code that exists only to match the default engine comes out: - revert #1662, #1655, #1624, #1622, #1614 and the dpkt hunks of #1605: the octet walk and its link-type stash, IPv6 in IPv4 and IPv4 in IPv6, refusing MPLS and ISL, the tunnel and extension-header walks, the IPv4 header checks and the layer bounds - keep #1690's timestamps, #1594's parser choice, #1621 and #1653 - re-land as guards: a layer dpkt's own lengths misplace (#1646) is re-serialised, not refused with ProtocolError; a Total Length below the IHL keeps the header whole (#1616), and a header the capture cuts is no input; packet2dict and packet2chain take a str payload and a raw frame - delete the modules #1662 and #1655 added; the 1615/1616 module keeps only what its #1616 tests use, as no scapy test imports it now; dpkt's comparisons with the default engine assert constants or the capture's octets, or are dropped - drop pypcapfile's VLAN_TAGS, VLAN_TAG_LEN and IPV6_EXTENSION_HEADERS, which only dpkt imported, and the 1668 module's _Agreement base, which nothing subclasses now - the docs drop the depth claims and name what dpkt leaves out silently The dpkt modules, the shared modules that run dpkt, tests/foundation/engines and the project guards pass on 3.14 and 3.11 (dpkt 1.9.8, pypcapfile 0.12.0).
Please follow the guide below
You will be asked some questions, please read them carefully and answer honestly
Put an
xinto all the boxes [ ] relevant to your pull request (like that [x])Use Preview tab to see how your pull request will actually look like
Searched for similar pull requests
Followed the coding style (
make pylint,make mypy,make isort)make testpasses, and a test case covers the changeAdded a changelog entry under
docs/source/changelog/and regeneratedCHANGELOG.md, if the change is user-visible — N/A — centralised in docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) #657What is the purpose of your pull request?
Tick the commit type your subject line carries.
fix— corrects a defectfeat— adds a featureperf— changes performance, not behaviourrefactor— changes neither behaviour nor performancetest— tests onlydocs— documentation onlyci— workflows or build toolingrelease— bumps the version or rolls up a distributionchore— anything elseDescription of your pull request and other information
Closes #1615.
Closes #1616.
Only
ipv4_reassemblyinpcapkit/toolkit/dpkt.pychanges, plus a new private helper_first_ipv4next to it.frame['IPv4']finds in the default engine. The walk handles IPv4 directly in IPv6, behind extension headers, and inside 6in6. The IPv4 octets stop at each IPv6 Payload Length above, so a trailer is left out. dpkt gives no input where the default engine dissects no IPv4: a later fragment, more than 32 extension headers (fix(protocols): a chain of ~80+ IPv6 extension headers exhausts recursion and leaves the payload undissected #1604), or a zero Payload Length with no Jumbo Payload option.tl - ihlitself.tests/toolkit/test_dpkt_ipv4_reassembly_input_1615_1616_unit.pychecks dpkt's inputs field by field against the default engine's over 21 built cases. It fails without the fix. Disabling the depth bound, the later-fragment check or the short-jumbogram check also turns it red.pylint: three new
C0415, from imports kept inside the helper. mypy: no new errors.