Skip to main content

packet_dissector_geneve/
lib.rs

1//! GENEVE (Generic Network Virtualization Encapsulation) dissector.
2//!
3//! ## References
4//! - RFC 8926: <https://www.rfc-editor.org/rfc/rfc8926>
5//!   - §3.4 Tunnel Header Fields: <https://www.rfc-editor.org/rfc/rfc8926#section-3.4>
6//!   - §3.5 Tunnel Options: <https://www.rfc-editor.org/rfc/rfc8926#section-3.5>
7
8#![deny(missing_docs)]
9
10use packet_dissector_core::dissector::{DispatchHint, DissectResult, Dissector};
11use packet_dissector_core::error::PacketError;
12use packet_dissector_core::field::{FieldDescriptor, FieldType, FieldValue};
13use packet_dissector_core::packet::DissectBuffer;
14use packet_dissector_core::util::{read_be_u16, read_be_u24};
15
16/// Minimum GENEVE header size (fixed header only, no options).
17///
18/// RFC 8926, Section 3.4 — The fixed tunnel header is 8 octets
19/// (Ver/Opt Len/O/C/Rsvd/Protocol Type/VNI/Reserved).
20/// <https://www.rfc-editor.org/rfc/rfc8926#section-3.4>
21const MIN_HEADER_SIZE: usize = 8;
22
23/// Field descriptor indices for [`GeneveDissector::field_descriptors`].
24const FD_VERSION: usize = 0;
25const FD_OPT_LEN: usize = 1;
26const FD_OAM: usize = 2;
27const FD_CRITICAL: usize = 3;
28const FD_RESERVED: usize = 4;
29const FD_PROTOCOL_TYPE: usize = 5;
30const FD_VNI: usize = 6;
31const FD_RESERVED2: usize = 7;
32const FD_OPTIONS: usize = 8;
33
34static FIELD_DESCRIPTORS: &[FieldDescriptor] = &[
35    FieldDescriptor::new("version", "Version", FieldType::U8),
36    FieldDescriptor::new("opt_len", "Options Length", FieldType::U8),
37    FieldDescriptor::new("oam", "OAM", FieldType::U8),
38    FieldDescriptor::new("critical", "Critical Options Present", FieldType::U8),
39    FieldDescriptor::new("reserved", "Reserved", FieldType::U8),
40    FieldDescriptor::new("protocol_type", "Protocol Type", FieldType::U16),
41    FieldDescriptor::new("vni", "Virtual Network Identifier", FieldType::U32),
42    FieldDescriptor::new("reserved2", "Reserved", FieldType::U8),
43    FieldDescriptor::new("options", "Options", FieldType::Bytes).optional(),
44];
45
46/// GENEVE dissector.
47pub struct GeneveDissector;
48
49impl Dissector for GeneveDissector {
50    fn name(&self) -> &'static str {
51        "Generic Network Virtualization Encapsulation"
52    }
53
54    fn short_name(&self) -> &'static str {
55        "GENEVE"
56    }
57
58    fn field_descriptors(&self) -> &'static [FieldDescriptor] {
59        FIELD_DESCRIPTORS
60    }
61
62    fn dissect<'pkt>(
63        &self,
64        data: &'pkt [u8],
65        buf: &mut DissectBuffer<'pkt>,
66        offset: usize,
67    ) -> Result<DissectResult, PacketError> {
68        if data.len() < MIN_HEADER_SIZE {
69            return Err(PacketError::Truncated {
70                expected: MIN_HEADER_SIZE,
71                actual: data.len(),
72            });
73        }
74
75        // RFC 8926, Section 3.4 — Version (2 bits, must be 0)
76        let version = (data[0] >> 6) & 0x03;
77        if version != 0 {
78            return Err(PacketError::InvalidFieldValue {
79                field: "version",
80                value: version as u32,
81            });
82        }
83
84        // RFC 8926, Section 3.4 — Opt Len (6 bits, in 4-byte multiples)
85        let opt_len = data[0] & 0x3F;
86        let header_len = MIN_HEADER_SIZE + (opt_len as usize) * 4;
87
88        if data.len() < header_len {
89            return Err(PacketError::Truncated {
90                expected: header_len,
91                actual: data.len(),
92            });
93        }
94
95        // RFC 8926, Section 3.4 — O (OAM) flag
96        let oam = (data[1] >> 7) & 1;
97        // RFC 8926, Section 3.4 — C (Critical) flag
98        let critical = (data[1] >> 6) & 1;
99        // RFC 8926, Section 3.4 — Reserved (6 bits)
100        let reserved = data[1] & 0x3F;
101
102        // RFC 8926, Section 3.4 — Protocol Type (EtherType)
103        let protocol_type = read_be_u16(data, 2)?;
104
105        // RFC 8926, Section 3.4 — VNI (24 bits)
106        let vni = read_be_u24(data, 4)?;
107
108        // RFC 8926, Section 3.4 — Reserved (8 bits)
109        let reserved2 = data[7];
110
111        buf.begin_layer(
112            self.short_name(),
113            None,
114            FIELD_DESCRIPTORS,
115            offset..offset + header_len,
116        );
117        buf.push_field(
118            &FIELD_DESCRIPTORS[FD_VERSION],
119            FieldValue::U8(version),
120            offset..offset + 1,
121        );
122        buf.push_field(
123            &FIELD_DESCRIPTORS[FD_OPT_LEN],
124            FieldValue::U8(opt_len),
125            offset..offset + 1,
126        );
127        buf.push_field(
128            &FIELD_DESCRIPTORS[FD_OAM],
129            FieldValue::U8(oam),
130            offset + 1..offset + 2,
131        );
132        buf.push_field(
133            &FIELD_DESCRIPTORS[FD_CRITICAL],
134            FieldValue::U8(critical),
135            offset + 1..offset + 2,
136        );
137        buf.push_field(
138            &FIELD_DESCRIPTORS[FD_RESERVED],
139            FieldValue::U8(reserved),
140            offset + 1..offset + 2,
141        );
142        buf.push_field(
143            &FIELD_DESCRIPTORS[FD_PROTOCOL_TYPE],
144            FieldValue::U16(protocol_type),
145            offset + 2..offset + 4,
146        );
147        buf.push_field(
148            &FIELD_DESCRIPTORS[FD_VNI],
149            FieldValue::U32(vni),
150            offset + 4..offset + 7,
151        );
152        buf.push_field(
153            &FIELD_DESCRIPTORS[FD_RESERVED2],
154            FieldValue::U8(reserved2),
155            offset + 7..offset + 8,
156        );
157
158        // RFC 8926, Section 3.5 — Tunnel Options (variable-length TLVs).
159        // The individual option TLVs are not parsed here; the raw option block
160        // is exposed verbatim for downstream inspection.
161        if opt_len > 0 {
162            buf.push_field(
163                &FIELD_DESCRIPTORS[FD_OPTIONS],
164                FieldValue::Bytes(&data[8..header_len]),
165                offset + 8..offset + header_len,
166            );
167        }
168        buf.end_layer();
169
170        Ok(DissectResult::new(
171            header_len,
172            DispatchHint::ByEtherType(protocol_type),
173        ))
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    // # RFC 8926 (GENEVE) Coverage
182    //
183    // | RFC Section | Description                     | Test                              |
184    // |-------------|---------------------------------|-----------------------------------|
185    // | 3.4         | Tunnel header fields            | parse_geneve_basic                |
186    // | 3.4         | Version validation (Ver != 0)   | parse_geneve_invalid_version      |
187    // | 3.4         | All invalid versions rejected   | parse_geneve_all_invalid_versions |
188    // | 3.4         | OAM (O) flag                    | parse_geneve_oam_flag             |
189    // | 3.4         | Critical (C) flag               | parse_geneve_critical_flag        |
190    // | 3.4         | Reserved bits ignored on recv   | parse_geneve_reserved_bits_set    |
191    // | 3.4         | VNI parsing (24 bits)           | parse_geneve_vni                  |
192    // | 3.4         | Protocol Type dispatch          | parse_geneve_dispatch_ipv6        |
193    // | 3.4         | Truncated fixed header          | parse_geneve_truncated            |
194    // | 3.4         | Offset handling                 | parse_geneve_with_offset          |
195    // | 3.5         | Variable-length options present | parse_geneve_with_options         |
196    // | 3.5         | Max Opt Len (63 × 4 bytes)      | parse_geneve_max_opt_len          |
197    // | 3.5         | Truncated options               | parse_geneve_truncated_options    |
198
199    /// Helper: dissect raw bytes at offset 0 and return the result.
200    fn dissect(data: &[u8]) -> Result<(DissectBuffer<'_>, DissectResult), PacketError> {
201        let mut buf = DissectBuffer::new();
202        let result = GeneveDissector.dissect(data, &mut buf, 0)?;
203        Ok((buf, result))
204    }
205
206    #[test]
207    fn parse_geneve_basic() {
208        // Minimal GENEVE header: Ver=0, OptLen=0, O=0, C=0,
209        // Protocol Type=0x6558 (Transparent Ethernet Bridging), VNI=1
210        let raw: &[u8] = &[
211            0x00, // Ver=0, OptLen=0
212            0x00, // O=0, C=0, Rsvd=0
213            0x65, 0x58, // Protocol Type: Transparent Ethernet Bridging
214            0x00, 0x00, 0x01, // VNI = 1
215            0x00, // Reserved
216        ];
217        let (buf, result) = dissect(raw).unwrap();
218        assert_eq!(result.bytes_consumed, 8);
219        assert_eq!(result.next, DispatchHint::ByEtherType(0x6558));
220
221        let layer = buf.layer_by_name("GENEVE").unwrap();
222        assert_eq!(
223            buf.field_by_name(layer, "version").unwrap().value,
224            FieldValue::U8(0)
225        );
226        assert_eq!(
227            buf.field_by_name(layer, "opt_len").unwrap().value,
228            FieldValue::U8(0)
229        );
230        assert_eq!(
231            buf.field_by_name(layer, "oam").unwrap().value,
232            FieldValue::U8(0)
233        );
234        assert_eq!(
235            buf.field_by_name(layer, "critical").unwrap().value,
236            FieldValue::U8(0)
237        );
238        assert_eq!(
239            buf.field_by_name(layer, "reserved").unwrap().value,
240            FieldValue::U8(0)
241        );
242        assert_eq!(
243            buf.field_by_name(layer, "protocol_type").unwrap().value,
244            FieldValue::U16(0x6558)
245        );
246        assert_eq!(
247            buf.field_by_name(layer, "vni").unwrap().value,
248            FieldValue::U32(1)
249        );
250        assert_eq!(
251            buf.field_by_name(layer, "reserved2").unwrap().value,
252            FieldValue::U8(0)
253        );
254        assert!(buf.field_by_name(layer, "options").is_none());
255    }
256
257    #[test]
258    fn parse_geneve_invalid_version() {
259        // Version = 1 (bits 6-7 of byte 0)
260        let raw: &[u8] = &[
261            0x40, // Ver=1, OptLen=0
262            0x00, // O=0, C=0
263            0x65, 0x58, // Protocol Type
264            0x00, 0x00, 0x01, // VNI
265            0x00, // Reserved
266        ];
267        let err = GeneveDissector
268            .dissect(raw, &mut DissectBuffer::new(), 0)
269            .unwrap_err();
270        assert!(matches!(
271            err,
272            PacketError::InvalidFieldValue {
273                field: "version",
274                value: 1
275            }
276        ));
277    }
278
279    #[test]
280    fn parse_geneve_with_options() {
281        // OptLen=2 → 8 bytes of options (2 × 4)
282        let raw: &[u8] = &[
283            0x02, // Ver=0, OptLen=2
284            0x00, // O=0, C=0
285            0x65, 0x58, // Protocol Type
286            0x00, 0x00, 0x0A, // VNI = 10
287            0x00, // Reserved
288            // Options: 8 bytes (2 × 4-byte words)
289            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
290        ];
291        let (buf, result) = dissect(raw).unwrap();
292        assert_eq!(result.bytes_consumed, 16);
293
294        let layer = buf.layer_by_name("GENEVE").unwrap();
295        assert_eq!(
296            buf.field_by_name(layer, "opt_len").unwrap().value,
297            FieldValue::U8(2)
298        );
299        assert_eq!(
300            buf.field_by_name(layer, "vni").unwrap().value,
301            FieldValue::U32(10)
302        );
303        assert_eq!(
304            buf.field_by_name(layer, "options").unwrap().value,
305            FieldValue::Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08])
306        );
307    }
308
309    #[test]
310    fn parse_geneve_oam_flag() {
311        // O=1
312        let raw: &[u8] = &[
313            0x00, // Ver=0, OptLen=0
314            0x80, // O=1, C=0
315            0x65, 0x58, // Protocol Type
316            0x00, 0x00, 0x01, // VNI
317            0x00, // Reserved
318        ];
319        let (buf, _) = dissect(raw).unwrap();
320        let layer = buf.layer_by_name("GENEVE").unwrap();
321        assert_eq!(
322            buf.field_by_name(layer, "oam").unwrap().value,
323            FieldValue::U8(1)
324        );
325        assert_eq!(
326            buf.field_by_name(layer, "critical").unwrap().value,
327            FieldValue::U8(0)
328        );
329    }
330
331    #[test]
332    fn parse_geneve_critical_flag() {
333        // C=1
334        let raw: &[u8] = &[
335            0x00, // Ver=0, OptLen=0
336            0x40, // O=0, C=1
337            0x65, 0x58, // Protocol Type
338            0x00, 0x00, 0x01, // VNI
339            0x00, // Reserved
340        ];
341        let (buf, _) = dissect(raw).unwrap();
342        let layer = buf.layer_by_name("GENEVE").unwrap();
343        assert_eq!(
344            buf.field_by_name(layer, "oam").unwrap().value,
345            FieldValue::U8(0)
346        );
347        assert_eq!(
348            buf.field_by_name(layer, "critical").unwrap().value,
349            FieldValue::U8(1)
350        );
351    }
352
353    #[test]
354    fn parse_geneve_vni() {
355        // VNI = 0xABCDEF
356        let raw: &[u8] = &[
357            0x00, // Ver=0, OptLen=0
358            0x00, // O=0, C=0
359            0x65, 0x58, // Protocol Type
360            0xAB, 0xCD, 0xEF, // VNI = 0xABCDEF
361            0x00, // Reserved
362        ];
363        let (buf, _) = dissect(raw).unwrap();
364        let layer = buf.layer_by_name("GENEVE").unwrap();
365        assert_eq!(
366            buf.field_by_name(layer, "vni").unwrap().value,
367            FieldValue::U32(0x00AB_CDEF)
368        );
369    }
370
371    #[test]
372    fn parse_geneve_dispatch_ipv6() {
373        // Protocol Type = 0x86DD (IPv6)
374        let raw: &[u8] = &[
375            0x00, // Ver=0, OptLen=0
376            0x00, // O=0, C=0
377            0x86, 0xDD, // Protocol Type: IPv6
378            0x00, 0x00, 0x01, // VNI
379            0x00, // Reserved
380        ];
381        let (_, result) = dissect(raw).unwrap();
382        assert_eq!(result.next, DispatchHint::ByEtherType(0x86DD));
383    }
384
385    #[test]
386    fn parse_geneve_truncated() {
387        let raw: &[u8] = &[0x00, 0x00, 0x65, 0x58, 0x00, 0x00, 0x01]; // 7 bytes
388        let err = GeneveDissector
389            .dissect(raw, &mut DissectBuffer::new(), 0)
390            .unwrap_err();
391        assert!(matches!(
392            err,
393            PacketError::Truncated {
394                expected: 8,
395                actual: 7
396            }
397        ));
398    }
399
400    #[test]
401    fn parse_geneve_truncated_options() {
402        // OptLen=1 but no option bytes present
403        let raw: &[u8] = &[
404            0x01, // Ver=0, OptLen=1
405            0x00, // O=0, C=0
406            0x65, 0x58, // Protocol Type
407            0x00, 0x00, 0x01, // VNI
408            0x00, // Reserved
409                  // Missing 4 bytes of options
410        ];
411        let err = GeneveDissector
412            .dissect(raw, &mut DissectBuffer::new(), 0)
413            .unwrap_err();
414        assert!(matches!(
415            err,
416            PacketError::Truncated {
417                expected: 12,
418                actual: 8
419            }
420        ));
421    }
422
423    #[test]
424    fn parse_geneve_with_offset() {
425        let raw: &[u8] = &[
426            0x01, // Ver=0, OptLen=1
427            0xC0, // O=1, C=1
428            0x65, 0x58, // Protocol Type
429            0x00, 0x00, 0x0A, // VNI = 10
430            0x00, // Reserved
431            0xAA, 0xBB, 0xCC, 0xDD, // Options (4 bytes)
432        ];
433        let mut buf = DissectBuffer::new();
434        let result = GeneveDissector.dissect(raw, &mut buf, 100).unwrap();
435        assert_eq!(result.bytes_consumed, 12);
436
437        let layer = buf.layer_by_name("GENEVE").unwrap();
438        assert_eq!(layer.range, 100..112);
439        assert_eq!(buf.field_by_name(layer, "version").unwrap().range, 100..101);
440        assert_eq!(
441            buf.field_by_name(layer, "protocol_type").unwrap().range,
442            102..104
443        );
444        assert_eq!(buf.field_by_name(layer, "vni").unwrap().range, 104..107);
445        assert_eq!(buf.field_by_name(layer, "options").unwrap().range, 108..112);
446    }
447
448    #[test]
449    fn parse_geneve_all_invalid_versions() {
450        // RFC 8926 §3.4: "Packets received by a tunnel endpoint with an unknown
451        // version MUST be dropped." Version is 2 bits — only 0 is defined.
452        for version in [1u8, 2, 3] {
453            let raw: &[u8] = &[
454                version << 6, // Ver in top 2 bits, OptLen=0
455                0x00,
456                0x65,
457                0x58,
458                0x00,
459                0x00,
460                0x01,
461                0x00,
462            ];
463            let err = GeneveDissector
464                .dissect(raw, &mut DissectBuffer::new(), 0)
465                .unwrap_err();
466            assert!(
467                matches!(
468                    err,
469                    PacketError::InvalidFieldValue {
470                        field: "version",
471                        value,
472                    } if value == u32::from(version)
473                ),
474                "expected InvalidFieldValue for Ver={version}, got {err:?}",
475            );
476        }
477    }
478
479    #[test]
480    fn parse_geneve_reserved_bits_set() {
481        // RFC 8926 §3.4: Rsvd. (6 bits) and Reserved (8 bits) "MUST be zero on
482        // transmission and MUST be ignored on receipt." A well-behaved dissector
483        // surfaces the bits as parsed values without rejecting the packet.
484        let raw: &[u8] = &[
485            0x00, // Ver=0, OptLen=0
486            0x3F, // O=0, C=0, Rsvd.=0x3F (all reserved bits set)
487            0x65, 0x58, // Protocol Type
488            0x00, 0x00, 0x01, // VNI
489            0xFF, // Reserved (8 bits) all set
490        ];
491        let (buf, _) = dissect(raw).unwrap();
492        let layer = buf.layer_by_name("GENEVE").unwrap();
493        assert_eq!(
494            buf.field_by_name(layer, "reserved").unwrap().value,
495            FieldValue::U8(0x3F)
496        );
497        assert_eq!(
498            buf.field_by_name(layer, "reserved2").unwrap().value,
499            FieldValue::U8(0xFF)
500        );
501        // OAM / Critical must not be affected by the high 2 bits already = 0.
502        assert_eq!(
503            buf.field_by_name(layer, "oam").unwrap().value,
504            FieldValue::U8(0)
505        );
506        assert_eq!(
507            buf.field_by_name(layer, "critical").unwrap().value,
508            FieldValue::U8(0)
509        );
510    }
511
512    #[test]
513    fn parse_geneve_max_opt_len() {
514        // RFC 8926 §3.4: "Opt Len (6 bits): The length of the option fields,
515        // expressed in 4-byte multiples, not including the 8-byte fixed tunnel
516        // header." The 6-bit field caps Opt Len at 63 (= 252 bytes of options),
517        // yielding a 260-byte total header.
518        const MAX_OPT_LEN_WORDS: u8 = 0x3F;
519        const OPTIONS_BYTES: usize = MAX_OPT_LEN_WORDS as usize * 4;
520        const TOTAL_HEADER: usize = 8 + OPTIONS_BYTES;
521
522        let mut raw = vec![0u8; TOTAL_HEADER];
523        raw[0] = MAX_OPT_LEN_WORDS; // Ver=0, OptLen=63
524        raw[1] = 0x00; // O=0, C=0
525        raw[2] = 0x65;
526        raw[3] = 0x58; // Protocol Type = TEB
527        raw[4] = 0x00;
528        raw[5] = 0x00;
529        raw[6] = 0x2A; // VNI=42
530        raw[7] = 0x00;
531        for (i, slot) in raw[8..].iter_mut().enumerate() {
532            *slot = (i & 0xFF) as u8;
533        }
534
535        let (buf, result) = dissect(&raw).unwrap();
536        assert_eq!(result.bytes_consumed, TOTAL_HEADER);
537        assert_eq!(result.next, DispatchHint::ByEtherType(0x6558));
538
539        let layer = buf.layer_by_name("GENEVE").unwrap();
540        assert_eq!(layer.range, 0..TOTAL_HEADER);
541        assert_eq!(
542            buf.field_by_name(layer, "opt_len").unwrap().value,
543            FieldValue::U8(MAX_OPT_LEN_WORDS)
544        );
545        let options = buf.field_by_name(layer, "options").unwrap();
546        assert_eq!(options.range, 8..TOTAL_HEADER);
547        assert_eq!(options.value, FieldValue::Bytes(&raw[8..TOTAL_HEADER]));
548    }
549
550    #[test]
551    fn field_descriptors_consistent() {
552        let descs = GeneveDissector.field_descriptors();
553        assert_eq!(descs.len(), 9);
554        assert_eq!(descs[FD_VERSION].name, "version");
555        assert_eq!(descs[FD_OPT_LEN].name, "opt_len");
556        assert_eq!(descs[FD_OAM].name, "oam");
557        assert_eq!(descs[FD_CRITICAL].name, "critical");
558        assert_eq!(descs[FD_RESERVED].name, "reserved");
559        assert_eq!(descs[FD_PROTOCOL_TYPE].name, "protocol_type");
560        assert_eq!(descs[FD_VNI].name, "vni");
561        assert_eq!(descs[FD_RESERVED2].name, "reserved2");
562        assert_eq!(descs[FD_OPTIONS].name, "options");
563    }
564}