Defeat IP-fragmentation evasion: wire flowscope ip_fragment::IpFragmentReassembler into the packet→tracker path #134

Closed
opened 2026-07-03 18:11:49 +00:00 by p13marc · 1 comment
p13marc commented 2026-07-03 18:11:49 +00:00 (Migrated from github.com)

Roadmap. Additive, opt-in; new capability from flowscope 0.22 (#132 bump is the prereq).

Problem

A fragmented IP datagram defeats every L4/L7 view netring builds today: the first fragment carries the L4 header (so the flow key classifies) but a parser sees a truncated payload, and non-first fragments have no L4 header at all — so deliberate fragmentation (a classic IDS-evasion technique, teardrop-style overlaps included) slips signatures, L7 parsers, and fingerprints. netring currently feeds each fragment to the tracker as-is.

What upstream provides

flowscope 0.22 ships flowscope::ip_fragment::IpFragmentReassembler (flowscope#138): RFC 791 keying (src, dst, protocol, id), offset-ordered reassembly, 30 s timeout, per-datagram + concurrent caps, and RFC 5722 overlap handling — overlapping fragments drop the whole datagram and increment overlaps() (itself a teardrop/evasion IOC). Transport-agnostic push + push_ipv4. IPv6 fragment-header decode is a documented upstream follow-up.

Proposal

  • Opt-in MonitorBuilder::reassemble_ip_fragments(bool) (default off — hot path pays nothing): in the run loop / flow streams, divert fragments into the reassembler and feed the reassembled datagram to the tracker/L7 path in place of the fragments.
  • Emit an anomaly (or at least a counter) from overlaps() — overlapping fragments on a modern network are almost always hostile.
  • Surface reassembler occupancy/timeouts through CaptureTelemetry/metrics so a fragment flood can't silently balloon state (interacts with #54 load-shedding).
  • Decide interaction with kernel-level BPF filters: a port-based kernel prefilter drops non-first fragments before userspace sees them — document that fragment reassembly wants the filter in fail-open/full-capture mode.

Scope notes

  • v4 first (matches upstream); v6 once flowscope lands the fragment-header decode.
  • Offline pcap path should get the same treatment (evasion analysis is a replay workload).
**Roadmap.** Additive, opt-in; new capability from flowscope 0.22 (#132 bump is the prereq). ## Problem A fragmented IP datagram defeats every L4/L7 view netring builds today: the first fragment carries the L4 header (so the flow key classifies) but a parser sees a truncated payload, and non-first fragments have no L4 header at all — so deliberate fragmentation (a classic IDS-evasion technique, teardrop-style overlaps included) slips signatures, L7 parsers, and fingerprints. netring currently feeds each fragment to the tracker as-is. ## What upstream provides flowscope 0.22 ships `flowscope::ip_fragment::IpFragmentReassembler` (flowscope#138): RFC 791 keying `(src, dst, protocol, id)`, offset-ordered reassembly, 30 s timeout, per-datagram + concurrent caps, and **RFC 5722 overlap handling — overlapping fragments drop the whole datagram** and increment `overlaps()` (itself a teardrop/evasion IOC). Transport-agnostic `push` + `push_ipv4`. IPv6 fragment-header decode is a documented upstream follow-up. ## Proposal - Opt-in `MonitorBuilder::reassemble_ip_fragments(bool)` (default off — hot path pays nothing): in the run loop / flow streams, divert fragments into the reassembler and feed the reassembled datagram to the tracker/L7 path in place of the fragments. - Emit an anomaly (or at least a counter) from `overlaps()` — overlapping fragments on a modern network are almost always hostile. - Surface reassembler occupancy/timeouts through `CaptureTelemetry`/metrics so a fragment flood can't silently balloon state (interacts with #54 load-shedding). - Decide interaction with kernel-level BPF filters: a port-based kernel prefilter drops non-first fragments before userspace sees them — document that fragment reassembly wants the filter in fail-open/full-capture mode. ## Scope notes - v4 first (matches upstream); v6 once flowscope lands the fragment-header decode. - Offline pcap path should get the same treatment (evasion analysis is a replay workload).
p13marc commented 2026-07-03 21:49:55 +00:00 (Migrated from github.com)

Status (0.29.0): building block landed, hot-path wiring deferred.

flowscope 0.22 (#132, done) ships ip_fragment::IpFragmentReassembler with a clean push_ipv4(&Ipv4Slice, now) -> Option<Vec<u8>> API (+ overlap/timeout/oversize counters and RFC 5722 overlap detection). That's the reassembly engine this issue needs.

The remaining work is genuinely hot-path and needs live/pcap validation, so it stays open rather than landing unvalidated:

  • Intercept at both run-loop track_into sites (live + replay), feeding fragments to the reassembler and swapping the tracker input (not taps/subs — those keep wire truth) for the reassembled datagram.
  • Frame synthesis: the reassembler returns the L3 payload; the tracker needs a full frame (prefix + patched IPv4 header with recomputed checksum + payload, VLAN preserved, >65535 guarded).
  • Kernel prefilter: an internal IP-fragment-allow atom unioned into kernel_prefilter() when armed (the cBPF port compiler currently drops non-first fragments), with the XDP .o port-map caveat documented.
  • Post-drain eviction + one ip_fragment_overlap Warning anomaly per overlap + netring_ip_frag_* metrics.

Strongest acceptance test (planned): a fragmented DNS response that parses only when reassembly is armed, with an off-by-default control. Deferred because this sandbox can't exercise the AF_PACKET/live path (see the 0.29.0 CHANGELOG 'Deferred' note).

**Status (0.29.0): building block landed, hot-path wiring deferred.** flowscope 0.22 (#132, done) ships `ip_fragment::IpFragmentReassembler` with a clean `push_ipv4(&Ipv4Slice, now) -> Option<Vec<u8>>` API (+ overlap/timeout/oversize counters and RFC 5722 overlap detection). That's the reassembly engine this issue needs. The remaining work is genuinely hot-path and needs live/pcap validation, so it stays open rather than landing unvalidated: - Intercept at both run-loop `track_into` sites (live + replay), feeding fragments to the reassembler and swapping the **tracker input** (not taps/subs — those keep wire truth) for the reassembled datagram. - **Frame synthesis**: the reassembler returns the L3 payload; the tracker needs a full frame (prefix + patched IPv4 header with recomputed checksum + payload, VLAN preserved, >65535 guarded). - **Kernel prefilter**: an internal IP-fragment-allow atom unioned into `kernel_prefilter()` when armed (the cBPF port compiler currently drops non-first fragments), with the XDP `.o` port-map caveat documented. - Post-drain eviction + one `ip_fragment_overlap` Warning anomaly per overlap + `netring_ip_frag_*` metrics. Strongest acceptance test (planned): a fragmented DNS response that parses **only** when reassembly is armed, with an off-by-default control. Deferred because this sandbox can't exercise the AF_PACKET/live path (see the 0.29.0 CHANGELOG 'Deferred' note).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
marcpardo/netring#134
No description provided.