diff options
Diffstat (limited to 'PLAN.md')
| -rw-r--r-- | PLAN.md | 44 |
1 files changed, 42 insertions, 2 deletions
@@ -30,8 +30,10 @@ unowned buffers) via a real-world capture pipeline. 3. [done] pcapng read/write 4. [done] Bounded channel + drop-on-backpressure between capture and render 5. [in progress] L7 dissector interface, add protocols incrementally -- - interface + DNS + HTTP + TLS SNI + mDNS + SSH banner done - (packeteer/l7/); more protocols can still be added incrementally, + interface + DNS + HTTP + TLS SNI + mDNS + SSH banner + NTP + DHCP + + FTP + SMTP + TFTP + QUIC done (packeteer/l7/); ARP/VLAN/IGMP done + at the L2/L3 level too (packeteer/net/); more protocols can still + be added incrementally, by design 6. [done] Filtering (-f <expr>, libpcap's own BPF compiler - see Decisions) 7. [done] Drop privileges after opening the capture handle (see Decisions) @@ -500,3 +502,41 @@ None currently open. re-verified against the same live traffic afterward, confirmed clean, and a regression test locks in the exact byte pattern that triggered it. +- QUIC (l7/quic.hpp), decoding only what RFC 9000 actually sends in + cleartext at the framing level: long vs. short header form, version, + long-packet type (Initial/0-RTT/Handshake/Retry/Version + Negotiation), and both connection IDs. Everything past that -- + packet numbers, frames, the payload - is encrypted from the first + protected byte onward, even for Initial packets (whose keys derive + via HKDF from a public per-version salt, then AES-GCM); actually + decrypting that is real crypto machinery this project deliberately + doesn't take on, the same call already made for TLS's SNI-only + extraction. A short-header packet's destination connection ID has no + length field in the packet itself - the receiver already knows it + from earlier connection state a passive observer doesn't have - so + short-header packets are reported by form alone. + Discovered and fixed a real, previously-latent bug while wiring this + in, caught by design review before it ever touched live traffic: + L7Registry::dissect() returned on the *first* dissector whose + port() matched, even if that dissector's summarize() then failed -- + harmless while every registered port was unique, but QUIC is the + first protocol here to genuinely share a well-known port with + something else already registered (443: TLS over TCP, QUIC over + UDP; the registry has no transport dimension, only a port number). + Without the fix, tls_dissector (registered first) would silently + claim every port-443 lookup and return nullopt for all QUIC traffic, + forever, regardless of registration order past it. Fixed to try each + same-port dissector until one actually succeeds; locked in with + tests/test_dissector.cpp using two stub dissectors on a shared port, + independent of any real protocol's parsing logic. + Live-verified about as thoroughly as anything in this project: real + HTTP/3 traffic to google.com via `curl --http3-only` (curl here + links ngtcp2/nghttp3), captured on wlp1s0, correctly decoding the + full connection lifecycle - Initial packets (including a genuine + connection ID migration mid-handshake, dcid changing from + a2d81abd... to e2d81abd..., real QUIC behavior, not a parsing + artifact), Handshake packets, and finally 1-RTT short-header + packets - against Google's actual production QUIC implementation. + This also serves as a real-traffic confirmation that the + L7Registry fix works: without it, none of this would have decoded + at all, since tls_dissector claims port 443 first. |