Suivi du chantier « un paquet encapsulé → plusieurs niveaux de flux ».
Tout passe par le module src/parse/tunnel/mod.rs.
Suivi GitHub : issue #15.
- Champ récursif
PacketFlow.inner: Option<Box<PacketFlow>>+flatten(). - Refactor
parse_impl→parse_layers(data_link, depth)(réutilisable). - Garde-fou profondeur (
MAX_TUNNEL_DEPTH = 4) + dégradation gracieuse (jamais d'erreur :None→ seule la ligne externe est produite). - CAPWAP-Data (UDP 5247) → IEEE 802.11 (ToDS/FromDS/WDS/QoS, gestion du byte-swap Frame Control Cisco) → LLC/SNAP → L3.
- Le tunnel est reporté comme protocole applicatif de la ligne externe
(ex.
"CAPWAP"). - 2 tests de référence sur trames réelles (ToDS/HLEN 16 et FromDS/HLEN 8).
Dans detect_inner() : reconnaître le tunnel, peler ses en-têtes, obtenir la
trame interne, puis appeler PacketFlow::parse_layers(inner_dl, depth + 1) et
renvoyer Some(("NOM", inner)).
Deux formes de trame interne :
- Interne = Ethernet (VXLAN, NVGRE, Geneve-transparent) : la trame interne
commence par un en-tête Ethernet complet →
PacketFlow::parse_layers(DataLink::try_from(inner_bytes)?, depth + 1). - Interne = IP brute (IP-in-IP, GTP-U, GRE-IP) : pas de L2 interne →
synthétiser un
DataLinkà MAC vides (MacAddress([0u8; 6])),ethertype= IPv4 (0x0800) ou IPv6 (0x86DD),payload= octets IP internes, puisparse_layers.
detect_inner() n'est appelé aujourd'hui que si transport est Some
(donc pour les tunnels au-dessus d'UDP/TCP). Les tunnels niveau IP
(GRE = proto 47, IP-in-IP = 4/41) ne produisent pas de couche Transport
→ il faudra ajouter un point de détection dans parse_layers quand
transport est None mais que internet.payload_protocol vaut Gre/Ipip.
Prévoir de passer internet (ou son payload + payload_protocol) à la
détection, pas seulement transport.
Ne rien merger sans une trame réelle en fixture (golden test), comme pour CAPWAP. Fournir le hex complet depuis l'Ethernet.
- VXLAN — UDP 4789. En-tête 8 octets (flags 1, réservé 3, VNI 3, réservé 1). Interne = Ethernet. Fixture requise.
- GTP-U — UDP 2152. En-tête variable (min 8 octets ; +4 si un des flags E/S/PN est posé ; puis extension headers). Interne = IP (pas de L2). Attention au champ « message type » = 255 (G-PDU) pour ne peler que les données. Fixture requise.
- Geneve — UDP 6081. En-tête 8 octets + options variables (Opt Len en
mots de 4).
protocol typeindique Ethernet (0x6558) ou IP. Fixture requise.
- IP-in-IP — IP proto 4 (IPv4) / 41 (IPv6). Interne = IP directe. Fixture requise.
- GRE — IP proto 47. En-tête 4 octets min (+ champs optionnels selon
les bits C/K/S) ; le champ
protocol type(EtherType) dit si l'interne est IP (0x0800/0x86DD) ou Ethernet (0x6558, NVGRE/transparent bridging). Fixture requise.
- CAPWAP-DTLS (préambule type 1) : chiffré → non décapsulable.
Déjà géré : on renvoie
None(pas de récursion), la ligne externe reste. À documenter comme limite, rien à coder. - ESP (proto 50) : chiffré → idem, ne pas récurser.
- MPLS (EtherType 0x8847/0x8848) : encapsulation niveau 2.5, à traiter
au niveau
DataLinksi besoin un jour.
- Re-vendorer la crate dans Sonar (
cargo vendor) après bump de version. - Dans le pipeline de capture, itérer
flatten()→ N lignes de flux par paquet. - Attribution des octets par niveau pour ne pas doubler le volume :
ligne externe = taille de la trame complète ; ligne interne = taille du
segment interne (
inner.data_link.payload.len()pour la couche L3 interne). Lecount(paquets) à +1 par niveau est correct.
- Décoder la trame en amont (Python/Wireshark) pour figer les offsets et les
adresses attendues, puis asserter externe et interne +
flatten().len(). - Couvrir les variantes qui changent le pelage (longueurs d'en-tête, sens du flux, présence d'options), comme les deux tests CAPWAP ToDS/FromDS.