1#![deny(missing_docs)]
11
12use packet_dissector_core::dissector::{DispatchHint, DissectResult, Dissector};
13use packet_dissector_core::error::PacketError;
14use packet_dissector_core::field::{FieldDescriptor, FieldType, FieldValue, FormatContext};
15use packet_dissector_core::packet::DissectBuffer;
16use packet_dissector_core::util::{read_be_u16, read_be_u32, read_ipv6_addr};
17
18const SRH_FIXED_SIZE: usize = 8;
21
22const SEGMENT_SIZE: usize = 16;
24
25const HMAC_TLV_MIN_VALUE_LEN: usize = 6;
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum CsidFlavor {
33 Classic,
35 ReplaceCsid {
40 csid_bits: u8,
42 },
43 NextCsid {
49 usid_bits: u8,
51 },
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum MobileSidEncoding {
65 EndMGtp4E {
70 ipv4da_bits: u8,
72 args_mob_session_bits: u8,
74 },
75 EndMGtp6E,
78 HmGtp4D {
81 prefix_bits: u8,
83 ipv4da_bits: u8,
85 args_mob_session_bits: u8,
87 },
88 EndLimit {
91 group_id_bits: u8,
93 limit_rate_bits: u8,
95 },
96}
97
98pub const BEHAVIOR_END_MAP: u16 = 40;
103pub const BEHAVIOR_END_LIMIT: u16 = 41;
105pub const BEHAVIOR_END_M_GTP6_D: u16 = 69;
107pub const BEHAVIOR_END_M_GTP6_DI: u16 = 70;
109pub const BEHAVIOR_END_M_GTP6_E: u16 = 71;
111pub const BEHAVIOR_END_M_GTP4_E: u16 = 72;
113
114pub fn endpoint_behavior_name(value: u16) -> Option<&'static str> {
117 match value {
118 BEHAVIOR_END_MAP => Some("End.MAP"),
119 BEHAVIOR_END_LIMIT => Some("End.Limit"),
120 BEHAVIOR_END_M_GTP6_D => Some("End.M.GTP6.D"),
121 BEHAVIOR_END_M_GTP6_DI => Some("End.M.GTP6.Di"),
122 BEHAVIOR_END_M_GTP6_E => Some("End.M.GTP6.E"),
123 BEHAVIOR_END_M_GTP4_E => Some("End.M.GTP4.E"),
124 _ => None,
125 }
126}
127
128#[derive(Debug, Clone)]
135pub struct SidStructure {
136 pub locator_block_bits: u8,
138 pub locator_node_bits: u8,
140 pub function_bits: u8,
142 pub argument_bits: u8,
144 pub csid_flavor: CsidFlavor,
146 pub mobile_encoding: Option<MobileSidEncoding>,
151}
152
153impl SidStructure {
154 pub fn new(
161 locator_block_bits: u8,
162 locator_node_bits: u8,
163 function_bits: u8,
164 argument_bits: u8,
165 csid_flavor: CsidFlavor,
166 ) -> Self {
167 SidStructure {
168 locator_block_bits,
169 locator_node_bits,
170 function_bits,
171 argument_bits,
172 csid_flavor,
173 mobile_encoding: None,
174 }
175 }
176}
177
178pub struct Srv6Dissector {
188 sid_structure: Option<SidStructure>,
189}
190
191impl Srv6Dissector {
192 pub fn new() -> Self {
194 Self {
195 sid_structure: None,
196 }
197 }
198
199 pub fn with_sid_structure(s: SidStructure) -> Self {
201 Self {
202 sid_structure: Some(s),
203 }
204 }
205}
206
207impl Default for Srv6Dissector {
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213fn parse_tlvs<'pkt>(
219 buf: &mut DissectBuffer<'pkt>,
220 data: &'pkt [u8],
221 offset: usize,
222 tlv_start: usize,
223 tlv_end: usize,
224) -> Result<(), PacketError> {
225 let mut cursor = tlv_start;
226
227 while cursor < tlv_end {
228 let type_byte = data[cursor];
229
230 if type_byte == 0 {
232 let obj_idx = buf.begin_container(
233 &FD_TLV,
234 FieldValue::Object(0..0),
235 offset + cursor..offset + cursor + 1,
236 );
237 buf.push_field(
238 &TLV_DESCRIPTORS[FD_TLV_TYPE],
239 FieldValue::U8(0),
240 offset + cursor..offset + cursor + 1,
241 );
242 buf.end_container(obj_idx);
243 cursor += 1;
244 continue;
245 }
246
247 if cursor + 1 >= tlv_end {
249 return Err(PacketError::Truncated {
250 expected: cursor + 2,
251 actual: tlv_end,
252 });
253 }
254
255 let length = data[cursor + 1] as usize;
256 let value_start = cursor + 2;
257 let value_end = value_start + length;
258
259 if value_end > tlv_end {
260 return Err(PacketError::Truncated {
261 expected: value_end,
262 actual: tlv_end,
263 });
264 }
265
266 let obj_idx = buf.begin_container(
267 &FD_TLV,
268 FieldValue::Object(0..0),
269 offset + cursor..offset + value_end,
270 );
271 buf.push_field(
272 &TLV_DESCRIPTORS[FD_TLV_TYPE],
273 FieldValue::U8(type_byte),
274 offset + cursor..offset + cursor + 1,
275 );
276 buf.push_field(
277 &TLV_DESCRIPTORS[FD_TLV_LENGTH],
278 FieldValue::U8(data[cursor + 1]),
279 offset + cursor + 1..offset + cursor + 2,
280 );
281
282 match type_byte {
283 4 => {
285 buf.push_field(
286 &TLV_DESCRIPTORS[FD_TLV_PADDING],
287 FieldValue::Bytes(&data[value_start..value_end]),
288 offset + value_start..offset + value_end,
289 );
290 }
291 5 if length >= HMAC_TLV_MIN_VALUE_LEN => {
293 let d_flag = (data[value_start] >> 7) & 1;
295 let key_id = read_be_u32(data, value_start + 2)?;
297 let hmac_start = value_start + 6;
298
299 buf.push_field(
300 &TLV_DESCRIPTORS[FD_TLV_HMAC_D_FLAG],
301 FieldValue::U8(d_flag),
302 offset + value_start..offset + value_start + 2,
303 );
304 buf.push_field(
305 &TLV_DESCRIPTORS[FD_TLV_HMAC_KEY_ID],
306 FieldValue::U32(key_id),
307 offset + value_start + 2..offset + value_start + 6,
308 );
309 if hmac_start < value_end {
310 buf.push_field(
311 &TLV_DESCRIPTORS[FD_TLV_HMAC],
312 FieldValue::Bytes(&data[hmac_start..value_end]),
313 offset + hmac_start..offset + value_end,
314 );
315 }
316 }
317 _ => {
319 buf.push_field(
320 &TLV_DESCRIPTORS[FD_TLV_VALUE],
321 FieldValue::Bytes(&data[value_start..value_end]),
322 offset + value_start..offset + value_end,
323 );
324 }
325 }
326
327 buf.end_container(obj_idx);
328 cursor = value_end;
329 }
330
331 Ok(())
332}
333
334const EXTRACT_BITS_MAX: usize = 16;
336
337fn extract_bits(data: &[u8], bit_offset: u16, bit_len: u8) -> ([u8; EXTRACT_BITS_MAX], usize) {
342 let mut result = [0u8; EXTRACT_BITS_MAX];
343 if bit_len == 0 {
344 return (result, 0);
345 }
346 let num_bytes = (bit_len as usize).div_ceil(8);
347 for i in 0..bit_len as u16 {
348 let src_bit = bit_offset + i;
349 let src_byte = (src_bit / 8) as usize;
350 let src_bit_pos = 7 - (src_bit % 8);
351 if src_byte < data.len() {
352 let bit_val = (data[src_byte] >> src_bit_pos) & 1;
353 let dst_bit = (bit_len as u16 - 1) - i;
354 let dst_byte = (dst_bit / 8) as usize;
355 let dst_bit_pos = dst_bit % 8;
356 result[num_bytes - 1 - dst_byte] |= bit_val << dst_bit_pos;
357 }
358 }
359 (result, num_bytes)
360}
361
362struct SidParts {
364 lb: ([u8; EXTRACT_BITS_MAX], usize),
365 ln: ([u8; EXTRACT_BITS_MAX], usize),
366 func: ([u8; EXTRACT_BITS_MAX], usize),
367 arg: ([u8; EXTRACT_BITS_MAX], usize),
368}
369
370fn extract_sid_parts(sid: &[u8], ss: &SidStructure) -> SidParts {
375 let mut bit_offset: u16 = 0;
376 let lb = extract_bits(sid, bit_offset, ss.locator_block_bits);
377 bit_offset += ss.locator_block_bits as u16;
378 let ln = extract_bits(sid, bit_offset, ss.locator_node_bits);
379 bit_offset += ss.locator_node_bits as u16;
380 let func = extract_bits(sid, bit_offset, ss.function_bits);
381 bit_offset += ss.function_bits as u16;
382 let arg = extract_bits(sid, bit_offset, ss.argument_bits);
383 SidParts { lb, ln, func, arg }
384}
385
386#[allow(clippy::too_many_arguments)]
393fn decompose_csid_containers<'pkt>(
394 buf: &mut DissectBuffer<'pkt>,
395 data: &'pkt [u8],
396 offset: usize,
397 num_segments: usize,
398 total_len: usize,
399 k: usize,
400 start_bit: usize,
401 slot_bits: usize,
402 csid_bits: u8,
403) {
404 for i in 0..num_segments {
405 let seg_start = SRH_FIXED_SIZE + i * SEGMENT_SIZE;
406 let seg_end = seg_start + SEGMENT_SIZE;
407 if seg_end > total_len {
408 break;
409 }
410 let sid = &data[seg_start..seg_end];
411 let seg_range = offset + seg_start..offset + seg_end;
412
413 let obj_idx = buf.begin_container(
415 &CSID_CONTAINER_DESCRIPTORS[FD_CONTAINER_INDEX],
416 FieldValue::Object(0..0),
417 seg_range.clone(),
418 );
419 buf.push_field(
420 &CSID_CONTAINER_DESCRIPTORS[FD_CONTAINER_INDEX],
421 FieldValue::U8(i as u8),
422 seg_range.clone(),
423 );
424 let arr_idx = buf.begin_container(
426 &CSID_CONTAINER_DESCRIPTORS[FD_CSIDS],
427 FieldValue::Array(0..0),
428 seg_range.clone(),
429 );
430 for slot in 0..k {
431 let bit_offset = (start_bit + slot * slot_bits) as u16;
432 let (csid_buf, csid_len) = extract_bits(sid, bit_offset, csid_bits);
433 let scratch_range = buf.push_scratch(&csid_buf[..csid_len]);
434 buf.push_field(
435 &CSID_CONTAINER_DESCRIPTORS[FD_CSIDS],
436 FieldValue::Scratch(scratch_range),
437 seg_range.clone(),
438 );
439 }
440 buf.end_container(arr_idx);
441 buf.end_container(obj_idx);
442 }
443}
444
445const ARGS_MOB_SESSION_MIN_BITS: u8 = 40;
449
450fn push_args_mob_session<'pkt>(
467 buf: &mut DissectBuffer<'pkt>,
468 sid: &[u8],
469 bit_offset: u16,
470 available_bits: u8,
471 offset: usize,
472 seg_range: std::ops::Range<usize>,
473) {
474 if available_bits < ARGS_MOB_SESSION_MIN_BITS {
475 return;
476 }
477
478 let (raw, _) = extract_bits(sid, bit_offset, ARGS_MOB_SESSION_MIN_BITS);
480 let qfi = (raw[0] >> 2) & 0x3F;
482 let r_flag = (raw[0] >> 1) & 1;
483 let u_flag = raw[0] & 1;
484 let pdu_session_id = read_be_u32(&raw, 1).unwrap_or_default();
485
486 let abs_range = offset + seg_range.start..offset + seg_range.end;
487 let obj_idx = buf.begin_container(
488 &AMS_PARENT_DESCRIPTORS[FD_ARGS_MOB_SESSION],
489 FieldValue::Object(0..0),
490 abs_range.clone(),
491 );
492 buf.push_field(
493 &AMS_DESCRIPTORS[FD_AMS_QFI],
494 FieldValue::U8(qfi),
495 abs_range.clone(),
496 );
497 buf.push_field(
498 &AMS_DESCRIPTORS[FD_AMS_R_FLAG],
499 FieldValue::U8(r_flag),
500 abs_range.clone(),
501 );
502 buf.push_field(
503 &AMS_DESCRIPTORS[FD_AMS_U_FLAG],
504 FieldValue::U8(u_flag),
505 abs_range.clone(),
506 );
507 buf.push_field(
508 &AMS_DESCRIPTORS[FD_AMS_PDU_SESSION_ID],
509 FieldValue::U32(pdu_session_id),
510 abs_range,
511 );
512 buf.end_container(obj_idx);
513}
514
515fn push_mobile_sid<'pkt>(
522 buf: &mut DissectBuffer<'pkt>,
523 sid: &[u8],
524 ss: &SidStructure,
525 encoding: &MobileSidEncoding,
526 offset: usize,
527 seg_range: std::ops::Range<usize>,
528) {
529 let abs_range = offset + seg_range.start..offset + seg_range.end;
530 let loc_func_bits =
531 ss.locator_block_bits as u16 + ss.locator_node_bits as u16 + ss.function_bits as u16;
532
533 fn push_embedded_ipv4(
538 buf: &mut DissectBuffer<'_>,
539 sid: &[u8],
540 bit_offset: u16,
541 ipv4da_bits: u8,
542 range: std::ops::Range<usize>,
543 ) {
544 let (raw, raw_len) = extract_bits(sid, bit_offset, ipv4da_bits);
545 if ipv4da_bits == 32 && bit_offset % 8 == 0 {
546 let mut addr = [0u8; 4];
547 for (i, b) in raw[..raw_len].iter().take(4).enumerate() {
548 addr[i] = *b;
549 }
550 buf.push_field(
551 &MOBILE_DESCRIPTORS[FD_MOBILE_EMBEDDED_IPV4],
552 FieldValue::Ipv4Addr(addr),
553 range,
554 );
555 } else {
556 let scratch_range = buf.push_scratch(&raw[..raw_len]);
557 buf.push_field(
558 &MOBILE_DESCRIPTORS[FD_MOBILE_EMBEDDED_IPV4],
559 FieldValue::Scratch(scratch_range),
560 range,
561 );
562 }
563 }
564
565 match encoding {
566 MobileSidEncoding::EndMGtp4E {
567 ipv4da_bits,
568 args_mob_session_bits,
569 } => {
570 if *ipv4da_bits > 0 {
573 push_embedded_ipv4(buf, sid, loc_func_bits, *ipv4da_bits, abs_range.clone());
574 }
575 let ams_offset = loc_func_bits + *ipv4da_bits as u16;
576 push_args_mob_session(
577 buf,
578 sid,
579 ams_offset,
580 *args_mob_session_bits,
581 offset,
582 seg_range,
583 );
584 }
585 MobileSidEncoding::EndMGtp6E => {
586 let arg_offset = loc_func_bits;
589 push_args_mob_session(buf, sid, arg_offset, ss.argument_bits, offset, seg_range);
590 }
591 MobileSidEncoding::HmGtp4D {
592 prefix_bits,
593 ipv4da_bits,
594 args_mob_session_bits,
595 } => {
596 let ipv4_start = *prefix_bits as u16;
599 if *ipv4da_bits > 0 {
600 push_embedded_ipv4(buf, sid, ipv4_start, *ipv4da_bits, abs_range.clone());
601 }
602 let ams_offset = ipv4_start + *ipv4da_bits as u16;
603 push_args_mob_session(
604 buf,
605 sid,
606 ams_offset,
607 *args_mob_session_bits,
608 offset,
609 seg_range,
610 );
611 }
612 MobileSidEncoding::EndLimit {
613 group_id_bits,
614 limit_rate_bits,
615 } => {
616 if *group_id_bits > 0 {
619 let (gid_buf, gid_len) = extract_bits(sid, loc_func_bits, *group_id_bits);
620 let scratch_range = buf.push_scratch(&gid_buf[..gid_len]);
621 buf.push_field(
622 &MOBILE_DESCRIPTORS[FD_MOBILE_GROUP_ID],
623 FieldValue::Scratch(scratch_range),
624 abs_range.clone(),
625 );
626 }
627 if *limit_rate_bits > 0 {
628 let lr_offset = loc_func_bits + *group_id_bits as u16;
629 let (lr_buf, lr_len) = extract_bits(sid, lr_offset, *limit_rate_bits);
630 let scratch_range = buf.push_scratch(&lr_buf[..lr_len]);
631 buf.push_field(
632 &MOBILE_DESCRIPTORS[FD_MOBILE_LIMIT_RATE],
633 FieldValue::Scratch(scratch_range),
634 abs_range,
635 );
636 }
637 }
638 }
639}
640
641const FD_NEXT_HEADER: usize = 0;
646const FD_HDR_EXT_LEN: usize = 1;
647const FD_ROUTING_TYPE: usize = 2;
648const FD_SEGMENTS_LEFT: usize = 3;
649const FD_LAST_ENTRY: usize = 4;
650const FD_FLAGS: usize = 5;
651const FD_TAG: usize = 6;
652const FD_SEGMENTS: usize = 7;
653const FD_SEGMENTS_STRUCTURE: usize = 8;
654const FD_CSID_CONTAINERS: usize = 9;
655const FD_TLVS: usize = 10;
656
657static FIELD_DESCRIPTORS: &[FieldDescriptor] = &[
659 FieldDescriptor::new("next_header", "Next Header", FieldType::U8),
660 FieldDescriptor::new("hdr_ext_len", "Header Extension Length", FieldType::U8),
661 FieldDescriptor::new("routing_type", "Routing Type", FieldType::U8),
662 FieldDescriptor::new("segments_left", "Segments Left", FieldType::U8),
663 FieldDescriptor::new("last_entry", "Last Entry", FieldType::U8),
664 FieldDescriptor::new("flags", "Flags", FieldType::Object),
665 FieldDescriptor::new("tag", "Tag", FieldType::U16),
666 FieldDescriptor::new("segments", "Segment List", FieldType::Array),
667 FieldDescriptor::new("segments_structure", "Segment Structure", FieldType::Array).optional(),
668 FieldDescriptor::new("csid_containers", "CSID Containers", FieldType::Array).optional(),
669 FieldDescriptor::new("tlvs", "TLVs", FieldType::Array).optional(),
670];
671
672fn srv6_tlv_type_name(t: u8) -> Option<&'static str> {
680 match t {
681 0 => Some("Pad1"),
682 4 => Some("PadN"),
683 5 => Some("HMAC"),
684 _ => None,
685 }
686}
687
688static FD_TLV: FieldDescriptor = FieldDescriptor {
693 name: "tlv",
694 display_name: "TLV",
695 field_type: FieldType::Object,
696 optional: false,
697 children: None,
698 display_fn: Some(|v, children| match v {
699 FieldValue::Object(_) => children.iter().find_map(|f| match (f.name(), &f.value) {
700 ("type", FieldValue::U8(t)) => srv6_tlv_type_name(*t),
701 _ => None,
702 }),
703 _ => None,
704 }),
705 format_fn: None,
706};
707
708const FD_TLV_TYPE: usize = 0;
710const FD_TLV_LENGTH: usize = 1;
711const FD_TLV_PADDING: usize = 2;
712const FD_TLV_HMAC_D_FLAG: usize = 3;
713const FD_TLV_HMAC_KEY_ID: usize = 4;
714const FD_TLV_HMAC: usize = 5;
715const FD_TLV_VALUE: usize = 6;
716
717static TLV_DESCRIPTORS: &[FieldDescriptor] = &[
718 FieldDescriptor::new("type", "Type", FieldType::U8),
719 FieldDescriptor::new("length", "Length", FieldType::U8),
720 FieldDescriptor::new("padding", "Padding", FieldType::Bytes).optional(),
721 FieldDescriptor::new("hmac_d_flag", "D Flag", FieldType::U8).optional(),
722 FieldDescriptor::new("hmac_key_id", "HMAC Key ID", FieldType::U32).optional(),
723 FieldDescriptor::new("hmac", "HMAC", FieldType::Bytes).optional(),
724 FieldDescriptor::new("value", "Value", FieldType::Bytes).optional(),
725];
726
727const FD_CONTAINER_INDEX: usize = 0;
729const FD_CSIDS: usize = 1;
730
731static CSID_CONTAINER_DESCRIPTORS: &[FieldDescriptor] = &[
732 FieldDescriptor::new("container_index", "Container Index", FieldType::U8),
733 FieldDescriptor::new("csids", "CSIDs", FieldType::Array),
734];
735
736const FD_AMS_QFI: usize = 0;
738const FD_AMS_R_FLAG: usize = 1;
739const FD_AMS_U_FLAG: usize = 2;
740const FD_AMS_PDU_SESSION_ID: usize = 3;
741
742static AMS_DESCRIPTORS: &[FieldDescriptor] = &[
743 FieldDescriptor::new("qfi", "QFI", FieldType::U8),
744 FieldDescriptor::new("r_flag", "R Flag", FieldType::U8),
745 FieldDescriptor::new("u_flag", "U Flag", FieldType::U8),
746 FieldDescriptor::new("pdu_session_id", "PDU Session ID", FieldType::U32),
747];
748
749const FD_ARGS_MOB_SESSION: usize = 0;
751
752static AMS_PARENT_DESCRIPTORS: &[FieldDescriptor] =
753 &[
754 FieldDescriptor::new("args_mob_session", "Args.Mob.Session", FieldType::Object)
755 .optional()
756 .with_children(AMS_DESCRIPTORS),
757 ];
758
759const FD_MOBILE_EMBEDDED_IPV4: usize = 0;
761const FD_MOBILE_GROUP_ID: usize = 1;
762const FD_MOBILE_LIMIT_RATE: usize = 2;
763
764static MOBILE_DESCRIPTORS: &[FieldDescriptor] = &[
765 FieldDescriptor::new("embedded_ipv4", "Embedded IPv4", FieldType::Ipv4Addr).optional(),
766 FieldDescriptor::new("group_id", "Group ID", FieldType::Bytes).optional(),
767 FieldDescriptor::new("limit_rate", "Limit Rate", FieldType::Bytes).optional(),
768];
769
770fn format_sid_hex(
775 value: &FieldValue<'_>,
776 ctx: &FormatContext<'_>,
777 w: &mut dyn std::io::Write,
778) -> std::io::Result<()> {
779 let bytes: &[u8] = match value {
780 FieldValue::Scratch(range) => &ctx.scratch[range.start as usize..range.end as usize],
781 FieldValue::Bytes(b) => b,
782 _ => return w.write_all(b"\"\""),
783 };
784 if bytes.is_empty() {
785 return w.write_all(b"\"\"");
786 }
787 w.write_all(b"\"")?;
788 for &b in bytes {
789 write!(w, "{b:02x}")?;
790 }
791 w.write_all(b"\"")
792}
793
794const FD_SID_LOCATOR_BLOCK: usize = 0;
796const FD_SID_LOCATOR_NODE: usize = 1;
797const FD_SID_FUNCTION: usize = 2;
798const FD_SID_ARGUMENT: usize = 3;
799
800static SID_STRUCTURE_DESCRIPTORS: &[FieldDescriptor] = &[
801 FieldDescriptor::new("locator_block", "Locator Block", FieldType::Bytes)
802 .with_format_fn(format_sid_hex),
803 FieldDescriptor::new("locator_node", "Locator Node", FieldType::Bytes)
804 .with_format_fn(format_sid_hex),
805 FieldDescriptor::new("function", "Function", FieldType::Bytes).with_format_fn(format_sid_hex),
806 FieldDescriptor::new("argument", "Argument", FieldType::Bytes).with_format_fn(format_sid_hex),
807];
808
809const FD_FLAGS_RAW: usize = 0;
811const FD_FLAGS_O_FLAG: usize = 1;
812
813static FLAGS_DESCRIPTORS: &[FieldDescriptor] = &[
814 FieldDescriptor::new("raw", "Raw", FieldType::U8),
815 FieldDescriptor::new("o_flag", "O Flag", FieldType::U8),
816];
817
818impl Dissector for Srv6Dissector {
819 fn name(&self) -> &'static str {
820 "IPv6 Segment Routing Header"
821 }
822
823 fn short_name(&self) -> &'static str {
824 "SRv6"
825 }
826
827 fn field_descriptors(&self) -> &'static [FieldDescriptor] {
828 FIELD_DESCRIPTORS
829 }
830
831 fn dissect<'pkt>(
832 &self,
833 data: &'pkt [u8],
834 buf: &mut DissectBuffer<'pkt>,
835 offset: usize,
836 ) -> Result<DissectResult, PacketError> {
837 if data.len() < SRH_FIXED_SIZE {
838 return Err(PacketError::Truncated {
839 expected: SRH_FIXED_SIZE,
840 actual: data.len(),
841 });
842 }
843
844 let next_header = data[0];
846 let hdr_ext_len = data[1];
847 let routing_type = data[2];
848 let segments_left = data[3];
849 let last_entry = data[4];
850 let flags = data[5];
851 let tag = read_be_u16(data, 6)?;
852
853 let total_len = (hdr_ext_len as usize + 1) * 8;
855
856 if data.len() < total_len {
857 return Err(PacketError::Truncated {
858 expected: total_len,
859 actual: data.len(),
860 });
861 }
862
863 let max_segs = hdr_ext_len as usize / 2;
869 if last_entry as usize >= max_segs {
870 return Err(PacketError::InvalidHeader(
871 "SRH Last Entry exceeds maximum for Hdr Ext Len",
872 ));
873 }
874
875 if segments_left as usize > last_entry as usize + 1 {
876 return Err(PacketError::InvalidHeader(
877 "SRH Segments Left exceeds Last Entry + 1",
878 ));
879 }
880
881 buf.begin_layer(
882 self.short_name(),
883 None,
884 FIELD_DESCRIPTORS,
885 offset..offset + total_len,
886 );
887
888 buf.push_field(
889 &FIELD_DESCRIPTORS[FD_NEXT_HEADER],
890 FieldValue::U8(next_header),
891 offset..offset + 1,
892 );
893 buf.push_field(
894 &FIELD_DESCRIPTORS[FD_HDR_EXT_LEN],
895 FieldValue::U8(hdr_ext_len),
896 offset + 1..offset + 2,
897 );
898 buf.push_field(
899 &FIELD_DESCRIPTORS[FD_ROUTING_TYPE],
900 FieldValue::U8(routing_type),
901 offset + 2..offset + 3,
902 );
903 buf.push_field(
904 &FIELD_DESCRIPTORS[FD_SEGMENTS_LEFT],
905 FieldValue::U8(segments_left),
906 offset + 3..offset + 4,
907 );
908 buf.push_field(
909 &FIELD_DESCRIPTORS[FD_LAST_ENTRY],
910 FieldValue::U8(last_entry),
911 offset + 4..offset + 5,
912 );
913
914 let flags_idx = buf.begin_container(
917 &FIELD_DESCRIPTORS[FD_FLAGS],
918 FieldValue::Object(0..0),
919 offset + 5..offset + 6,
920 );
921 buf.push_field(
922 &FLAGS_DESCRIPTORS[FD_FLAGS_RAW],
923 FieldValue::U8(flags),
924 offset + 5..offset + 6,
925 );
926 buf.push_field(
927 &FLAGS_DESCRIPTORS[FD_FLAGS_O_FLAG],
928 FieldValue::U8((flags >> 5) & 1),
929 offset + 5..offset + 6,
930 );
931 buf.end_container(flags_idx);
932
933 buf.push_field(
934 &FIELD_DESCRIPTORS[FD_TAG],
935 FieldValue::U16(tag),
936 offset + 6..offset + 8,
937 );
938
939 let num_segments = last_entry as usize + 1;
943 let mut actual_segments = 0usize;
944 let seg_arr_idx = buf.begin_container(
945 &FIELD_DESCRIPTORS[FD_SEGMENTS],
946 FieldValue::Array(0..0),
947 offset + SRH_FIXED_SIZE..offset + SRH_FIXED_SIZE + num_segments * SEGMENT_SIZE,
948 );
949 for i in 0..num_segments {
950 let seg_start = SRH_FIXED_SIZE + i * SEGMENT_SIZE;
951 let seg_end = seg_start + SEGMENT_SIZE;
952 if seg_end > total_len {
953 break;
954 }
955 let addr = read_ipv6_addr(data, seg_start)?;
956 buf.push_field(
957 &FIELD_DESCRIPTORS[FD_SEGMENTS],
958 FieldValue::Ipv6Addr(addr),
959 offset + seg_start..offset + seg_end,
960 );
961 actual_segments += 1;
962 }
963 buf.end_container(seg_arr_idx);
964
965 let seg_range_end = SRH_FIXED_SIZE + actual_segments * SEGMENT_SIZE;
966
967 if let Some(ref ss) = self.sid_structure {
969 let struct_arr_idx = buf.begin_container(
970 &FIELD_DESCRIPTORS[FD_SEGMENTS_STRUCTURE],
971 FieldValue::Array(0..0),
972 offset + SRH_FIXED_SIZE..offset + seg_range_end,
973 );
974 for i in 0..num_segments {
975 let seg_start = SRH_FIXED_SIZE + i * SEGMENT_SIZE;
976 let seg_end = seg_start + SEGMENT_SIZE;
977 if seg_end > total_len {
978 break;
979 }
980 let sid = &data[seg_start..seg_end];
981 let parts = extract_sid_parts(sid, ss);
982 let seg_range = offset + seg_start..offset + seg_end;
983
984 let obj_idx = buf.begin_container(
985 &SID_STRUCTURE_DESCRIPTORS[FD_SID_LOCATOR_BLOCK],
986 FieldValue::Object(0..0),
987 seg_range.clone(),
988 );
989
990 let lb_scratch = buf.push_scratch(&parts.lb.0[..parts.lb.1]);
991 buf.push_field(
992 &SID_STRUCTURE_DESCRIPTORS[FD_SID_LOCATOR_BLOCK],
993 FieldValue::Scratch(lb_scratch),
994 seg_range.clone(),
995 );
996 let ln_scratch = buf.push_scratch(&parts.ln.0[..parts.ln.1]);
997 buf.push_field(
998 &SID_STRUCTURE_DESCRIPTORS[FD_SID_LOCATOR_NODE],
999 FieldValue::Scratch(ln_scratch),
1000 seg_range.clone(),
1001 );
1002 let func_scratch = buf.push_scratch(&parts.func.0[..parts.func.1]);
1003 buf.push_field(
1004 &SID_STRUCTURE_DESCRIPTORS[FD_SID_FUNCTION],
1005 FieldValue::Scratch(func_scratch),
1006 seg_range.clone(),
1007 );
1008 let arg_scratch = buf.push_scratch(&parts.arg.0[..parts.arg.1]);
1009 buf.push_field(
1010 &SID_STRUCTURE_DESCRIPTORS[FD_SID_ARGUMENT],
1011 FieldValue::Scratch(arg_scratch),
1012 seg_range.clone(),
1013 );
1014
1015 if let Some(ref enc) = ss.mobile_encoding {
1017 push_mobile_sid(buf, sid, ss, enc, offset, seg_start..seg_end);
1018 }
1019
1020 buf.end_container(obj_idx);
1021 }
1022 buf.end_container(struct_arr_idx);
1023
1024 let lnfl = ss.locator_node_bits as usize + ss.function_bits as usize;
1026 #[allow(clippy::manual_checked_ops)]
1029 if lnfl > 0 {
1030 match &ss.csid_flavor {
1031 CsidFlavor::ReplaceCsid { csid_bits } if *csid_bits > 0 => {
1032 let k = 128 / lnfl;
1034 let csid_arr_idx = buf.begin_container(
1035 &FIELD_DESCRIPTORS[FD_CSID_CONTAINERS],
1036 FieldValue::Array(0..0),
1037 offset + SRH_FIXED_SIZE..offset + seg_range_end,
1038 );
1039 decompose_csid_containers(
1040 buf,
1041 data,
1042 offset,
1043 num_segments,
1044 total_len,
1045 k,
1046 0, lnfl,
1048 *csid_bits,
1049 );
1050 buf.end_container(csid_arr_idx);
1051 }
1052 CsidFlavor::NextCsid { usid_bits } if *usid_bits > 0 => {
1053 let lbl = ss.locator_block_bits as usize;
1057 if lbl < 128 {
1058 let k = (128 - lbl) / lnfl;
1059 let csid_arr_idx = buf.begin_container(
1060 &FIELD_DESCRIPTORS[FD_CSID_CONTAINERS],
1061 FieldValue::Array(0..0),
1062 offset + SRH_FIXED_SIZE..offset + seg_range_end,
1063 );
1064 decompose_csid_containers(
1065 buf,
1066 data,
1067 offset,
1068 num_segments,
1069 total_len,
1070 k,
1071 lbl, lnfl,
1073 *usid_bits,
1074 );
1075 buf.end_container(csid_arr_idx);
1076 }
1077 }
1078 _ => {}
1079 }
1080 }
1081 }
1082
1083 let tlv_start = SRH_FIXED_SIZE + num_segments * SEGMENT_SIZE;
1085 if tlv_start < total_len {
1086 let tlv_arr_idx = buf.begin_container(
1087 &FIELD_DESCRIPTORS[FD_TLVS],
1088 FieldValue::Array(0..0),
1089 offset + tlv_start..offset + total_len,
1090 );
1091 parse_tlvs(buf, data, offset, tlv_start, total_len)?;
1092 buf.end_container(tlv_arr_idx);
1093 }
1094
1095 buf.end_layer();
1096
1097 Ok(DissectResult::new(
1098 total_len,
1099 DispatchHint::ByIpProtocol(next_header),
1100 ))
1101 }
1102}
1103
1104#[cfg(test)]
1105mod tests {
1106 use super::*;
1107 use packet_dissector_core::field::{Field, FormatContext};
1108 use packet_dissector_core::packet::Layer;
1109
1110 fn nested_field_by_name<'a, 'pkt>(
1112 buf: &'a DissectBuffer<'pkt>,
1113 parent: &Field<'pkt>,
1114 name: &str,
1115 ) -> Option<&'a Field<'pkt>> {
1116 let range = parent.value.as_container_range()?;
1117 buf.nested_fields(range).iter().find(|f| f.name() == name)
1118 }
1119
1120 fn resolve_scratch<'a>(buf: &'a DissectBuffer<'_>, field: &Field<'_>) -> &'a [u8] {
1122 let range = field
1123 .value
1124 .as_scratch_range()
1125 .expect("expected Scratch value");
1126 &buf.scratch()[range.start as usize..range.end as usize]
1127 }
1128
1129 fn array_entries<'a, 'pkt>(
1131 buf: &'a DissectBuffer<'pkt>,
1132 array_field: &Field<'pkt>,
1133 ) -> Vec<&'a Field<'pkt>> {
1134 let range = array_field.value.as_container_range().unwrap();
1135 let all = buf.nested_fields(range);
1136 let base = range.start;
1137 let mut result = Vec::new();
1138 let mut abs_idx = base;
1139 while abs_idx < range.end {
1140 let rel = (abs_idx - base) as usize;
1141 let field = &all[rel];
1142 result.push(field);
1143 if let Some(child_range) = field.value.as_container_range() {
1145 abs_idx = child_range.end;
1147 } else {
1148 abs_idx += 1;
1149 }
1150 }
1151 result
1152 }
1153
1154 const SID_A: [u8; 16] = [
1190 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1191 0x01,
1192 ];
1193 const SID_B: [u8; 16] = [
1194 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1195 0x02,
1196 ];
1197 const SID_C: [u8; 16] = [
1198 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1199 0x03,
1200 ];
1201
1202 fn build_srh(
1204 next_header: u8,
1205 segments_left: u8,
1206 segments: &[[u8; 16]],
1207 flags: u8,
1208 tag: u16,
1209 tlvs: &[u8],
1210 ) -> Vec<u8> {
1211 let num_segments = segments.len();
1212 let seg_bytes = num_segments * SEGMENT_SIZE;
1214 let tlv_padded_len = if tlvs.is_empty() {
1215 0
1216 } else {
1217 let raw_total = SRH_FIXED_SIZE + seg_bytes + tlvs.len();
1219 let padded_total = (raw_total + 7) & !7;
1220 padded_total - SRH_FIXED_SIZE - seg_bytes
1221 };
1222 let total_len = SRH_FIXED_SIZE + seg_bytes + tlv_padded_len;
1223 let hdr_ext_len = (total_len / 8) - 1;
1224 let last_entry = if num_segments == 0 {
1225 0
1226 } else {
1227 (num_segments - 1) as u8
1228 };
1229
1230 let mut data = Vec::with_capacity(total_len);
1231 data.push(next_header);
1232 data.push(hdr_ext_len as u8);
1233 data.push(4); data.push(segments_left);
1235 data.push(last_entry);
1236 data.push(flags);
1237 data.extend_from_slice(&tag.to_be_bytes());
1238
1239 for seg in segments {
1240 data.extend_from_slice(seg);
1241 }
1242
1243 if !tlvs.is_empty() {
1244 data.extend_from_slice(tlvs);
1245 data.resize(total_len, 0);
1247 }
1248
1249 data
1250 }
1251
1252 #[test]
1253 fn srv6_dissector_metadata() {
1254 let d = Srv6Dissector::new();
1255 assert_eq!(d.name(), "IPv6 Segment Routing Header");
1256 assert_eq!(d.short_name(), "SRv6");
1257 }
1258
1259 #[test]
1260 fn parse_srv6_single_segment() {
1261 let data = build_srh(6, 1, &[SID_A], 0, 0, &[]);
1263 let mut buf = DissectBuffer::new();
1264 let result = Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1265
1266 assert_eq!(result.bytes_consumed, 24);
1267 assert_eq!(result.next, DispatchHint::ByIpProtocol(6));
1268
1269 let layer = &buf.layers()[0];
1270 assert_eq!(layer.name, "SRv6");
1271 assert_eq!(
1272 buf.field_by_name(layer, "next_header").unwrap().value,
1273 FieldValue::U8(6)
1274 );
1275 assert_eq!(
1276 buf.field_by_name(layer, "hdr_ext_len").unwrap().value,
1277 FieldValue::U8(2)
1278 );
1279 assert_eq!(
1280 buf.field_by_name(layer, "routing_type").unwrap().value,
1281 FieldValue::U8(4)
1282 );
1283 assert_eq!(
1284 buf.field_by_name(layer, "segments_left").unwrap().value,
1285 FieldValue::U8(1)
1286 );
1287 assert_eq!(
1288 buf.field_by_name(layer, "last_entry").unwrap().value,
1289 FieldValue::U8(0)
1290 );
1291 let segments = {
1292 let r = buf
1293 .field_by_name(layer, "segments")
1294 .unwrap()
1295 .value
1296 .as_container_range()
1297 .unwrap();
1298 buf.nested_fields(r)
1299 };
1300 assert_eq!(segments.len(), 1);
1301 assert_eq!(segments[0].value, FieldValue::Ipv6Addr(SID_A));
1302 assert!(buf.field_by_name(layer, "tlvs").is_none());
1303 }
1304
1305 #[test]
1306 fn parse_srv6_multiple_segments() {
1307 let data = build_srh(17, 2, &[SID_A, SID_B, SID_C], 0, 0, &[]);
1309 let mut buf = DissectBuffer::new();
1310 let result = Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1311
1312 assert_eq!(result.bytes_consumed, 56);
1313 assert_eq!(result.next, DispatchHint::ByIpProtocol(17));
1314
1315 let layer = &buf.layers()[0];
1316 assert_eq!(
1317 buf.field_by_name(layer, "last_entry").unwrap().value,
1318 FieldValue::U8(2)
1319 );
1320 assert_eq!(
1321 buf.field_by_name(layer, "segments_left").unwrap().value,
1322 FieldValue::U8(2)
1323 );
1324 let segments = {
1325 let r = buf
1326 .field_by_name(layer, "segments")
1327 .unwrap()
1328 .value
1329 .as_container_range()
1330 .unwrap();
1331 buf.nested_fields(r)
1332 };
1333 assert_eq!(segments.len(), 3);
1334 assert_eq!(segments[0].value, FieldValue::Ipv6Addr(SID_A));
1335 assert_eq!(segments[1].value, FieldValue::Ipv6Addr(SID_B));
1336 assert_eq!(segments[2].value, FieldValue::Ipv6Addr(SID_C));
1337 }
1338
1339 fn flags_sub_field<'a, 'pkt>(
1341 buf: &'a DissectBuffer<'pkt>,
1342 layer: &Layer,
1343 name: &str,
1344 ) -> Option<&'a Field<'pkt>> {
1345 let flags_field = buf.field_by_name(layer, "flags")?;
1346 let range = flags_field.value.as_container_range()?;
1347 buf.nested_fields(range).iter().find(|f| f.name() == name)
1348 }
1349
1350 #[test]
1351 fn parse_srv6_flags_and_tag() {
1352 let data = build_srh(59, 0, &[SID_A], 0xAB, 0x1234, &[]);
1354 let mut buf = DissectBuffer::new();
1355 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1356
1357 let layer = &buf.layers()[0];
1358 assert_eq!(
1360 flags_sub_field(&buf, layer, "raw").unwrap().value,
1361 FieldValue::U8(0xAB)
1362 );
1363 assert_eq!(
1365 flags_sub_field(&buf, layer, "o_flag").unwrap().value,
1366 FieldValue::U8(1)
1367 );
1368 assert_eq!(
1369 buf.field_by_name(layer, "tag").unwrap().value,
1370 FieldValue::U16(0x1234)
1371 );
1372 }
1373
1374 #[test]
1375 fn parse_srv6_flags_o_flag_set() {
1376 let data = build_srh(6, 1, &[SID_A], 0x20, 0, &[]);
1378 let mut buf = DissectBuffer::new();
1379 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1380
1381 let layer = &buf.layers()[0];
1382 assert_eq!(
1383 flags_sub_field(&buf, layer, "raw").unwrap().value,
1384 FieldValue::U8(0x20)
1385 );
1386 assert_eq!(
1387 flags_sub_field(&buf, layer, "o_flag").unwrap().value,
1388 FieldValue::U8(1)
1389 );
1390 }
1391
1392 #[test]
1393 fn parse_srv6_flags_all_zero() {
1394 let data = build_srh(6, 1, &[SID_A], 0x00, 0, &[]);
1395 let mut buf = DissectBuffer::new();
1396 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1397
1398 let layer = &buf.layers()[0];
1399 assert_eq!(
1400 flags_sub_field(&buf, layer, "raw").unwrap().value,
1401 FieldValue::U8(0)
1402 );
1403 assert_eq!(
1404 flags_sub_field(&buf, layer, "o_flag").unwrap().value,
1405 FieldValue::U8(0)
1406 );
1407 }
1408
1409 #[test]
1410 fn parse_srv6_with_tlvs() {
1411 let tlvs = [4, 2, 0x00, 0x00]; let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
1416 let mut buf = DissectBuffer::new();
1417 let result = Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1418
1419 assert_eq!(result.bytes_consumed, 32);
1420
1421 let layer = &buf.layers()[0];
1422 let tlvs_field = buf.field_by_name(layer, "tlvs").unwrap();
1424 let tlvs = array_entries(&buf, tlvs_field);
1425 let tlv0 = tlvs[0];
1426 assert_eq!(
1427 tlv_sub_field(&buf, tlv0, "type").unwrap().value,
1428 FieldValue::U8(4)
1429 );
1430 assert_eq!(
1431 tlv_sub_field(&buf, tlv0, "length").unwrap().value,
1432 FieldValue::U8(2)
1433 );
1434 assert_eq!(
1435 tlv_sub_field(&buf, tlv0, "padding").unwrap().value,
1436 FieldValue::Bytes(&[0, 0])
1437 );
1438 }
1439
1440 #[test]
1441 fn parse_srv6_with_offset() {
1442 let data = build_srh(6, 1, &[SID_A], 0, 0, &[]);
1443 let mut buf = DissectBuffer::new();
1444 let result = Srv6Dissector::new().dissect(&data, &mut buf, 200).unwrap();
1445
1446 assert_eq!(result.bytes_consumed, 24);
1447 let layer = &buf.layers()[0];
1448 assert_eq!(layer.range, 200..224);
1449 assert_eq!(
1450 buf.field_by_name(layer, "next_header").unwrap().range,
1451 200..201
1452 );
1453 let segments = {
1454 let r = buf
1455 .field_by_name(layer, "segments")
1456 .unwrap()
1457 .value
1458 .as_container_range()
1459 .unwrap();
1460 buf.nested_fields(r)
1461 };
1462 assert_eq!(segments[0].range, 208..224);
1463 }
1464
1465 #[test]
1466 fn parse_srv6_truncated_header() {
1467 let data = [6, 2, 4, 1, 0, 0]; let mut buf = DissectBuffer::new();
1469 let err = Srv6Dissector::new()
1470 .dissect(&data, &mut buf, 0)
1471 .unwrap_err();
1472 assert!(matches!(
1473 err,
1474 PacketError::Truncated {
1475 expected: 8,
1476 actual: 6
1477 }
1478 ));
1479 }
1480
1481 #[test]
1482 fn parse_srv6_truncated_total() {
1483 let mut data = vec![6, 2, 4, 1, 0, 0, 0, 0]; data.extend_from_slice(&[0u8; 8]); let mut buf = DissectBuffer::new();
1487 let err = Srv6Dissector::new()
1488 .dissect(&data, &mut buf, 0)
1489 .unwrap_err();
1490 assert!(matches!(
1491 err,
1492 PacketError::Truncated {
1493 expected: 24,
1494 actual: 16
1495 }
1496 ));
1497 }
1498
1499 #[test]
1500 fn parse_srv6_invalid_last_entry() {
1501 let mut data = vec![0u8; 24];
1504 data[0] = 6; data[1] = 2; data[2] = 4; data[3] = 0; data[4] = 1; let mut buf = DissectBuffer::new();
1510 let err = Srv6Dissector::new()
1511 .dissect(&data, &mut buf, 0)
1512 .unwrap_err();
1513 assert!(matches!(err, PacketError::InvalidHeader(_)));
1514 }
1515
1516 #[test]
1517 fn parse_srv6_invalid_hdr_ext_len_small() {
1518 let mut data = vec![0u8; 8];
1523 data[0] = 6; data[1] = 0; data[2] = 4; data[3] = 0; data[4] = 0; let mut buf = DissectBuffer::new();
1529 let err = Srv6Dissector::new()
1530 .dissect(&data, &mut buf, 0)
1531 .unwrap_err();
1532 assert!(matches!(err, PacketError::InvalidHeader(_)));
1533
1534 let mut data2 = vec![0u8; 16];
1536 data2[0] = 6;
1537 data2[1] = 1; data2[2] = 4;
1539 data2[3] = 0;
1540 data2[4] = 0; let mut buf2 = DissectBuffer::new();
1542 let err2 = Srv6Dissector::new()
1543 .dissect(&data2, &mut buf2, 0)
1544 .unwrap_err();
1545 assert!(matches!(err2, PacketError::InvalidHeader(_)));
1546 }
1547
1548 #[test]
1549 fn parse_srv6_invalid_segments_left() {
1550 let mut data = vec![0u8; 24];
1552 data[0] = 6;
1553 data[1] = 2;
1554 data[2] = 4;
1555 data[3] = 2; data[4] = 0; let mut buf = DissectBuffer::new();
1558 let err = Srv6Dissector::new()
1559 .dissect(&data, &mut buf, 0)
1560 .unwrap_err();
1561 assert!(matches!(err, PacketError::InvalidHeader(_)));
1562 }
1563
1564 #[test]
1565 fn parse_srv6_next_header_tcp() {
1566 let data = build_srh(6, 0, &[SID_A], 0, 0, &[]);
1568 let mut buf = DissectBuffer::new();
1569 let result = Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1570 assert_eq!(result.next, DispatchHint::ByIpProtocol(6));
1571 }
1572
1573 #[test]
1574 fn parse_srv6_next_header_no_next() {
1575 let data = build_srh(59, 0, &[SID_A], 0, 0, &[]);
1577 let mut buf = DissectBuffer::new();
1578 let result = Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1579 assert_eq!(result.next, DispatchHint::ByIpProtocol(59));
1580 }
1581
1582 fn tlv_sub_field<'a, 'pkt>(
1586 buf: &'a DissectBuffer<'pkt>,
1587 tlv: &Field<'pkt>,
1588 name: &str,
1589 ) -> Option<&'a Field<'pkt>> {
1590 let range = tlv.value.as_container_range()?;
1591 buf.nested_fields(range).iter().find(|f| f.name() == name)
1592 }
1593
1594 #[test]
1595 fn parse_srv6_tlv_pad1() {
1596 let tlvs = [0x00, 4, 5, 0, 0, 0, 0, 0];
1600 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
1601 let mut buf = DissectBuffer::new();
1602 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1603
1604 let layer = &buf.layers()[0];
1605 let tlvs_field = buf.field_by_name(layer, "tlvs").unwrap();
1606 let tlvs = array_entries(&buf, tlvs_field);
1607 assert!(tlvs.len() >= 2);
1608 assert_eq!(
1610 tlv_sub_field(&buf, tlvs[0], "type").unwrap().value,
1611 FieldValue::U8(0)
1612 );
1613 assert!(tlv_sub_field(&buf, tlvs[0], "length").is_none());
1615 assert_eq!(
1617 tlv_sub_field(&buf, tlvs[1], "type").unwrap().value,
1618 FieldValue::U8(4)
1619 );
1620 assert_eq!(
1621 tlv_sub_field(&buf, tlvs[1], "length").unwrap().value,
1622 FieldValue::U8(5)
1623 );
1624 }
1625
1626 #[test]
1627 fn parse_srv6_tlv_padn() {
1628 let tlvs = [4, 4, 0, 0, 0, 0];
1631 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
1632 let mut buf = DissectBuffer::new();
1633 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1634
1635 let layer = &buf.layers()[0];
1636 let tlvs_field = buf.field_by_name(layer, "tlvs").unwrap();
1637 let tlvs = array_entries(&buf, tlvs_field);
1638 let tlv0 = tlvs[0];
1639 assert_eq!(
1640 tlv_sub_field(&buf, tlv0, "type").unwrap().value,
1641 FieldValue::U8(4)
1642 );
1643 assert_eq!(
1644 tlv_sub_field(&buf, tlv0, "length").unwrap().value,
1645 FieldValue::U8(4)
1646 );
1647 assert_eq!(
1648 tlv_sub_field(&buf, tlv0, "padding").unwrap().value,
1649 FieldValue::Bytes(&[0, 0, 0, 0])
1650 );
1651 }
1652
1653 #[test]
1654 fn parse_srv6_tlv_hmac() {
1655 let mut tlv_bytes = vec![5u8, 38]; tlv_bytes.extend_from_slice(&[0x80, 0x00]);
1661 tlv_bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
1663 let hmac_value: Vec<u8> = (1..=32).collect();
1665 tlv_bytes.extend_from_slice(&hmac_value);
1666
1667 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlv_bytes);
1668 let mut buf = DissectBuffer::new();
1669 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1670
1671 let layer = &buf.layers()[0];
1672 let tlvs_field = buf.field_by_name(layer, "tlvs").unwrap();
1673 let tlvs = array_entries(&buf, tlvs_field);
1674 let tlv0 = tlvs[0];
1675 assert_eq!(
1676 tlv_sub_field(&buf, tlv0, "type").unwrap().value,
1677 FieldValue::U8(5)
1678 );
1679 assert_eq!(
1680 tlv_sub_field(&buf, tlv0, "length").unwrap().value,
1681 FieldValue::U8(38)
1682 );
1683 assert_eq!(
1684 tlv_sub_field(&buf, tlv0, "hmac_d_flag").unwrap().value,
1685 FieldValue::U8(1)
1686 );
1687 assert_eq!(
1688 tlv_sub_field(&buf, tlv0, "hmac_key_id").unwrap().value,
1689 FieldValue::U32(1)
1690 );
1691 assert_eq!(
1692 tlv_sub_field(&buf, tlv0, "hmac").unwrap().value,
1693 FieldValue::Bytes(&hmac_value)
1694 );
1695 }
1696
1697 #[test]
1698 fn parse_srv6_tlv_hmac_d_flag_clear() {
1699 let mut tlv_bytes = vec![5u8, 38];
1701 tlv_bytes.extend_from_slice(&[0x00, 0x00]);
1703 tlv_bytes.extend_from_slice(&[0x12, 0x34, 0x56, 0x78]);
1705 tlv_bytes.extend_from_slice(&[0xFF; 32]);
1707
1708 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlv_bytes);
1709 let mut buf = DissectBuffer::new();
1710 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1711
1712 let layer = &buf.layers()[0];
1713 let tlvs_field = buf.field_by_name(layer, "tlvs").unwrap();
1714 let tlvs = array_entries(&buf, tlvs_field);
1715 let tlv0 = tlvs[0];
1716 assert_eq!(
1717 tlv_sub_field(&buf, tlv0, "hmac_d_flag").unwrap().value,
1718 FieldValue::U8(0)
1719 );
1720 assert_eq!(
1721 tlv_sub_field(&buf, tlv0, "hmac_key_id").unwrap().value,
1722 FieldValue::U32(0x12345678)
1723 );
1724 }
1725
1726 #[test]
1727 fn parse_srv6_tlv_unknown() {
1728 let tlvs = [99, 3, 0xAA, 0xBB, 0xCC];
1731 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
1732 let mut buf = DissectBuffer::new();
1733 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1734
1735 let layer = &buf.layers()[0];
1736 let tlvs_field = buf.field_by_name(layer, "tlvs").unwrap();
1737 let tlvs = array_entries(&buf, tlvs_field);
1738 let tlv0 = tlvs[0];
1739 assert_eq!(
1740 tlv_sub_field(&buf, tlv0, "type").unwrap().value,
1741 FieldValue::U8(99)
1742 );
1743 assert_eq!(
1744 tlv_sub_field(&buf, tlv0, "length").unwrap().value,
1745 FieldValue::U8(3)
1746 );
1747 assert_eq!(
1748 tlv_sub_field(&buf, tlv0, "value").unwrap().value,
1749 FieldValue::Bytes(&[0xAA, 0xBB, 0xCC])
1750 );
1751 }
1752
1753 #[test]
1754 fn parse_srv6_tlv_multiple() {
1755 let tlvs = [0x00, 4, 1, 0x00, 0x00];
1759 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
1760 let mut buf = DissectBuffer::new();
1761 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1762
1763 let layer = &buf.layers()[0];
1764 let tlvs_field = buf.field_by_name(layer, "tlvs").unwrap();
1765 let tlvs = array_entries(&buf, tlvs_field);
1766 assert!(tlvs.len() >= 2);
1767 assert_eq!(
1769 tlv_sub_field(&buf, tlvs[0], "type").unwrap().value,
1770 FieldValue::U8(0)
1771 );
1772 assert_eq!(
1774 tlv_sub_field(&buf, tlvs[1], "type").unwrap().value,
1775 FieldValue::U8(4)
1776 );
1777 assert_eq!(
1778 tlv_sub_field(&buf, tlvs[1], "length").unwrap().value,
1779 FieldValue::U8(1)
1780 );
1781 }
1782
1783 #[test]
1784 fn parse_srv6_tlv_truncated_length() {
1785 let tlvs = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 99];
1792 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
1793 let mut buf = DissectBuffer::new();
1794 let err = Srv6Dissector::new()
1795 .dissect(&data, &mut buf, 0)
1796 .unwrap_err();
1797 assert!(matches!(err, PacketError::Truncated { .. }));
1798 }
1799
1800 #[test]
1801 fn parse_srv6_tlv_truncated_value() {
1802 let tlvs = [99, 20, 0xAA, 0xBB, 0xCC, 0xDD];
1804 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
1805 let mut buf = DissectBuffer::new();
1806 let err = Srv6Dissector::new()
1807 .dissect(&data, &mut buf, 0)
1808 .unwrap_err();
1809 assert!(matches!(err, PacketError::Truncated { .. }));
1810 }
1811
1812 #[test]
1813 fn parse_srv6_tlv_hmac_truncated() {
1814 let tlvs = [5, 4, 0x80, 0x00, 0x00, 0x01];
1818 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
1819 let mut buf = DissectBuffer::new();
1820 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1821
1822 let layer = &buf.layers()[0];
1823 let tlvs_field = buf.field_by_name(layer, "tlvs").unwrap();
1824 let tlvs = array_entries(&buf, tlvs_field);
1825 let tlv0 = tlvs[0];
1826 assert_eq!(
1827 tlv_sub_field(&buf, tlv0, "type").unwrap().value,
1828 FieldValue::U8(5)
1829 );
1830 assert_eq!(
1831 tlv_sub_field(&buf, tlv0, "length").unwrap().value,
1832 FieldValue::U8(4)
1833 );
1834 assert_eq!(
1836 tlv_sub_field(&buf, tlv0, "value").unwrap().value,
1837 FieldValue::Bytes(&[0x80, 0x00, 0x00, 0x01])
1838 );
1839 assert!(tlv_sub_field(&buf, tlv0, "hmac_d_flag").is_none());
1841 }
1842
1843 #[test]
1861 fn parse_srv6_sid_structure_none() {
1862 let data = build_srh(6, 1, &[SID_A], 0, 0, &[]);
1864 let mut buf = DissectBuffer::new();
1865 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
1866
1867 let layer = &buf.layers()[0];
1868 assert!(buf.field_by_name(layer, "segments_structure").is_none());
1869 }
1870
1871 #[test]
1872 fn parse_srv6_sid_structure_48_16_16_48() {
1873 let ss = SidStructure {
1880 locator_block_bits: 48,
1881 locator_node_bits: 16,
1882 function_bits: 16,
1883 argument_bits: 48,
1884 csid_flavor: CsidFlavor::Classic,
1885 mobile_encoding: None,
1886 };
1887 let dissector = Srv6Dissector::with_sid_structure(ss);
1888 let data = build_srh(6, 1, &[SID_A], 0, 0, &[]);
1889 let mut buf = DissectBuffer::new();
1890 dissector.dissect(&data, &mut buf, 0).unwrap();
1891
1892 let layer = &buf.layers()[0];
1893 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
1894 let structure = array_entries(&buf, structure_field);
1895 assert_eq!(structure.len(), 1);
1896
1897 let seg0 = structure[0];
1898 assert_eq!(
1899 resolve_scratch(
1900 &buf,
1901 nested_field_by_name(&buf, seg0, "locator_block").unwrap()
1902 ),
1903 &[0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01]
1904 );
1905 assert_eq!(
1906 resolve_scratch(
1907 &buf,
1908 nested_field_by_name(&buf, seg0, "locator_node").unwrap()
1909 ),
1910 &[0x00, 0x00]
1911 );
1912 assert_eq!(
1913 resolve_scratch(&buf, nested_field_by_name(&buf, seg0, "function").unwrap()),
1914 &[0x00, 0x00]
1915 );
1916 assert_eq!(
1917 resolve_scratch(&buf, nested_field_by_name(&buf, seg0, "argument").unwrap()),
1918 &[0x00, 0x00, 0x00, 0x00, 0x00, 0x01]
1919 );
1920 }
1921
1922 #[test]
1925 fn parse_srv6_csid_disabled() {
1926 let ss = SidStructure {
1928 locator_block_bits: 48,
1929 locator_node_bits: 16,
1930 function_bits: 16,
1931 argument_bits: 48,
1932 csid_flavor: CsidFlavor::Classic,
1933 mobile_encoding: None,
1934 };
1935 let dissector = Srv6Dissector::with_sid_structure(ss);
1936 let data = build_srh(6, 1, &[SID_A], 0, 0, &[]);
1937 let mut buf = DissectBuffer::new();
1938 dissector.dissect(&data, &mut buf, 0).unwrap();
1939
1940 let layer = &buf.layers()[0];
1941 assert!(buf.field_by_name(layer, "segments_structure").is_some());
1943 assert!(buf.field_by_name(layer, "csid_containers").is_none());
1945 }
1946
1947 #[test]
1948 fn parse_srv6_csid_replace_csid() {
1949 let container: [u8; 16] = [
1960 0xAA, 0xAA, 0xBB, 0xBB, 0xCC, 0xCC, 0xDD, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1961 0x00, 0x00,
1962 ];
1963 let ss = SidStructure {
1964 locator_block_bits: 32,
1965 locator_node_bits: 16,
1966 function_bits: 16,
1967 argument_bits: 64,
1968 csid_flavor: CsidFlavor::ReplaceCsid { csid_bits: 32 },
1969 mobile_encoding: None,
1970 };
1971 let dissector = Srv6Dissector::with_sid_structure(ss);
1972 let data = build_srh(6, 1, &[container], 0, 0, &[]);
1973 let mut buf = DissectBuffer::new();
1974 dissector.dissect(&data, &mut buf, 0).unwrap();
1975
1976 let layer = &buf.layers()[0];
1977 let containers_field = buf.field_by_name(layer, "csid_containers").unwrap();
1978 let containers = array_entries(&buf, containers_field);
1979 assert_eq!(containers.len(), 1);
1980
1981 let c0 = containers[0];
1982 assert_eq!(
1983 nested_field_by_name(&buf, c0, "container_index")
1984 .unwrap()
1985 .value,
1986 FieldValue::U8(0)
1987 );
1988 let csids_field = nested_field_by_name(&buf, c0, "csids").unwrap();
1989 let csids = buf.nested_fields(csids_field.value.as_container_range().unwrap());
1990 assert_eq!(csids.len(), 4);
1991 assert_eq!(resolve_scratch(&buf, &csids[0]), &[0xAA, 0xAA, 0xBB, 0xBB]);
1993 assert_eq!(resolve_scratch(&buf, &csids[1]), &[0xCC, 0xCC, 0xDD, 0xDD]);
1995 assert_eq!(resolve_scratch(&buf, &csids[2]), &[0x00, 0x00, 0x00, 0x00]);
1997 assert_eq!(resolve_scratch(&buf, &csids[3]), &[0x00, 0x00, 0x00, 0x00]);
1999 }
2000
2001 #[test]
2002 fn parse_srv6_usid_next_csid() {
2003 let container: [u8; 16] = [
2013 0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0x11, 0x11, 0xCC, 0xCC, 0x22, 0x22, 0xDD, 0xDD, 0x33, 0x33, ];
2018 let ss = SidStructure {
2019 locator_block_bits: 32,
2020 locator_node_bits: 16,
2021 function_bits: 16,
2022 argument_bits: 64,
2023 csid_flavor: CsidFlavor::NextCsid { usid_bits: 32 },
2024 mobile_encoding: None,
2025 };
2026 let dissector = Srv6Dissector::with_sid_structure(ss);
2027 let data = build_srh(6, 1, &[container], 0, 0, &[]);
2028 let mut buf = DissectBuffer::new();
2029 dissector.dissect(&data, &mut buf, 0).unwrap();
2030
2031 let layer = &buf.layers()[0];
2032 let containers_field = buf.field_by_name(layer, "csid_containers").unwrap();
2033 let containers = array_entries(&buf, containers_field);
2034 assert_eq!(containers.len(), 1);
2035
2036 let c0 = containers[0];
2037 let csids_field = nested_field_by_name(&buf, c0, "csids").unwrap();
2038 let csids = buf.nested_fields(csids_field.value.as_container_range().unwrap());
2039 assert_eq!(csids.len(), 3);
2041 assert_eq!(resolve_scratch(&buf, &csids[0]), &[0xBB, 0xBB, 0x11, 0x11]);
2043 assert_eq!(resolve_scratch(&buf, &csids[1]), &[0xCC, 0xCC, 0x22, 0x22]);
2045 assert_eq!(resolve_scratch(&buf, &csids[2]), &[0xDD, 0xDD, 0x33, 0x33]);
2047 }
2048
2049 #[test]
2065 fn endpoint_behavior_names() {
2066 assert_eq!(endpoint_behavior_name(BEHAVIOR_END_MAP), Some("End.MAP"));
2067 assert_eq!(
2068 endpoint_behavior_name(BEHAVIOR_END_LIMIT),
2069 Some("End.Limit")
2070 );
2071 assert_eq!(
2072 endpoint_behavior_name(BEHAVIOR_END_M_GTP6_D),
2073 Some("End.M.GTP6.D")
2074 );
2075 assert_eq!(
2076 endpoint_behavior_name(BEHAVIOR_END_M_GTP6_DI),
2077 Some("End.M.GTP6.Di")
2078 );
2079 assert_eq!(
2080 endpoint_behavior_name(BEHAVIOR_END_M_GTP6_E),
2081 Some("End.M.GTP6.E")
2082 );
2083 assert_eq!(
2084 endpoint_behavior_name(BEHAVIOR_END_M_GTP4_E),
2085 Some("End.M.GTP4.E")
2086 );
2087 assert_eq!(endpoint_behavior_name(0), None);
2088 assert_eq!(endpoint_behavior_name(9999), None);
2089 }
2090
2091 #[test]
2092 fn parse_srv6_args_mob_session_basic() {
2093 let sid: [u8; 16] = [
2106 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x47, 0x24, 0x12, 0x34, 0x56, 0x78, 0x00, ];
2112 let ss = SidStructure {
2113 locator_block_bits: 48,
2114 locator_node_bits: 16,
2115 function_bits: 16,
2116 argument_bits: 48,
2117 csid_flavor: CsidFlavor::Classic,
2118 mobile_encoding: Some(MobileSidEncoding::EndMGtp6E),
2119 };
2120 let dissector = Srv6Dissector::with_sid_structure(ss);
2121 let data = build_srh(6, 1, &[sid], 0, 0, &[]);
2122 let mut buf = DissectBuffer::new();
2123 dissector.dissect(&data, &mut buf, 0).unwrap();
2124
2125 let layer = &buf.layers()[0];
2126 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2127 let structure = array_entries(&buf, structure_field);
2128 assert_eq!(structure.len(), 1);
2129
2130 let seg0 = structure[0];
2131 assert!(nested_field_by_name(&buf, seg0, "locator_block").is_some());
2133 assert!(nested_field_by_name(&buf, seg0, "function").is_some());
2134
2135 let ams = nested_field_by_name(&buf, seg0, "args_mob_session").unwrap();
2137 let ams_obj = buf.nested_fields(ams.value.as_container_range().unwrap());
2138 let qfi = ams_obj.iter().find(|f| f.name() == "qfi").unwrap();
2139 assert_eq!(qfi.value, FieldValue::U8(9));
2140 let r_flag = ams_obj.iter().find(|f| f.name() == "r_flag").unwrap();
2141 assert_eq!(r_flag.value, FieldValue::U8(0));
2142 let u_flag = ams_obj.iter().find(|f| f.name() == "u_flag").unwrap();
2143 assert_eq!(u_flag.value, FieldValue::U8(0));
2144 let pdu_id = ams_obj
2145 .iter()
2146 .find(|f| f.name() == "pdu_session_id")
2147 .unwrap();
2148 assert_eq!(pdu_id.value, FieldValue::U32(0x12345678));
2149 }
2150
2151 #[test]
2152 fn parse_srv6_args_mob_session_max_qfi() {
2153 let sid: [u8; 16] = [
2156 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x47, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, ];
2162 let ss = SidStructure {
2163 locator_block_bits: 48,
2164 locator_node_bits: 16,
2165 function_bits: 16,
2166 argument_bits: 48,
2167 csid_flavor: CsidFlavor::Classic,
2168 mobile_encoding: Some(MobileSidEncoding::EndMGtp6E),
2169 };
2170 let dissector = Srv6Dissector::with_sid_structure(ss);
2171 let data = build_srh(6, 1, &[sid], 0, 0, &[]);
2172 let mut buf = DissectBuffer::new();
2173 dissector.dissect(&data, &mut buf, 0).unwrap();
2174
2175 let layer = &buf.layers()[0];
2176 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2177 let structure = array_entries(&buf, structure_field);
2178 let seg0 = structure[0];
2179 let ams = nested_field_by_name(&buf, seg0, "args_mob_session").unwrap();
2180 let ams_obj = buf.nested_fields(ams.value.as_container_range().unwrap());
2181 let qfi = ams_obj.iter().find(|f| f.name() == "qfi").unwrap();
2182 assert_eq!(qfi.value, FieldValue::U8(63));
2183 }
2184
2185 #[test]
2186 fn parse_srv6_args_mob_session_r_flag() {
2187 let sid: [u8; 16] = [
2190 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x47, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0x00, ];
2196 let ss = SidStructure {
2197 locator_block_bits: 48,
2198 locator_node_bits: 16,
2199 function_bits: 16,
2200 argument_bits: 48,
2201 csid_flavor: CsidFlavor::Classic,
2202 mobile_encoding: Some(MobileSidEncoding::EndMGtp6E),
2203 };
2204 let dissector = Srv6Dissector::with_sid_structure(ss);
2205 let data = build_srh(6, 1, &[sid], 0, 0, &[]);
2206 let mut buf = DissectBuffer::new();
2207 dissector.dissect(&data, &mut buf, 0).unwrap();
2208
2209 let layer = &buf.layers()[0];
2210 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2211 let structure = array_entries(&buf, structure_field);
2212 let seg0 = structure[0];
2213 let ams = nested_field_by_name(&buf, seg0, "args_mob_session").unwrap();
2214 let ams_obj = buf.nested_fields(ams.value.as_container_range().unwrap());
2215 let qfi = ams_obj.iter().find(|f| f.name() == "qfi").unwrap();
2216 assert_eq!(qfi.value, FieldValue::U8(0));
2217 let r_flag = ams_obj.iter().find(|f| f.name() == "r_flag").unwrap();
2218 assert_eq!(r_flag.value, FieldValue::U8(1));
2219 let u_flag = ams_obj.iter().find(|f| f.name() == "u_flag").unwrap();
2220 assert_eq!(u_flag.value, FieldValue::U8(0));
2221 let pdu_id = ams_obj
2222 .iter()
2223 .find(|f| f.name() == "pdu_session_id")
2224 .unwrap();
2225 assert_eq!(pdu_id.value, FieldValue::U32(0xAABBCCDD));
2226 }
2227
2228 #[test]
2229 fn parse_srv6_args_mob_session_too_short() {
2230 let sid: [u8; 16] = [
2234 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x47, 0x00, 0x01, 0x24, 0x12, 0x34, 0x56, ];
2239 let ss = SidStructure {
2240 locator_block_bits: 48,
2241 locator_node_bits: 16,
2242 function_bits: 32,
2243 argument_bits: 32,
2244 csid_flavor: CsidFlavor::Classic,
2245 mobile_encoding: Some(MobileSidEncoding::EndMGtp6E),
2246 };
2247 let dissector = Srv6Dissector::with_sid_structure(ss);
2248 let data = build_srh(6, 1, &[sid], 0, 0, &[]);
2249 let mut buf = DissectBuffer::new();
2250 dissector.dissect(&data, &mut buf, 0).unwrap();
2251
2252 let layer = &buf.layers()[0];
2253 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2254 let structure = array_entries(&buf, structure_field);
2255 let seg0 = structure[0];
2256 assert!(nested_field_by_name(&buf, seg0, "args_mob_session").is_none());
2258 }
2259
2260 #[test]
2261 fn parse_srv6_mobile_end_m_gtp4_e() {
2262 let sid: [u8; 16] = [
2269 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x0A, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x42, 0x00, ];
2275 let ss = SidStructure {
2276 locator_block_bits: 32,
2277 locator_node_bits: 0,
2278 function_bits: 16,
2279 argument_bits: 80,
2280 csid_flavor: CsidFlavor::Classic,
2281 mobile_encoding: Some(MobileSidEncoding::EndMGtp4E {
2282 ipv4da_bits: 32,
2283 args_mob_session_bits: 40,
2284 }),
2285 };
2286 let dissector = Srv6Dissector::with_sid_structure(ss);
2287 let data = build_srh(6, 1, &[sid], 0, 0, &[]);
2288 let mut buf = DissectBuffer::new();
2289 dissector.dissect(&data, &mut buf, 0).unwrap();
2290
2291 let layer = &buf.layers()[0];
2292 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2293 let structure = array_entries(&buf, structure_field);
2294 let seg0 = structure[0];
2295
2296 let ipv4 = nested_field_by_name(&buf, seg0, "embedded_ipv4").unwrap();
2298 assert_eq!(ipv4.value, FieldValue::Ipv4Addr([10, 0, 0, 1]));
2299
2300 let ams = nested_field_by_name(&buf, seg0, "args_mob_session").unwrap();
2302 let ams_obj = buf.nested_fields(ams.value.as_container_range().unwrap());
2303 let qfi = ams_obj.iter().find(|f| f.name() == "qfi").unwrap();
2304 assert_eq!(qfi.value, FieldValue::U8(5));
2305 let pdu_id = ams_obj
2306 .iter()
2307 .find(|f| f.name() == "pdu_session_id")
2308 .unwrap();
2309 assert_eq!(pdu_id.value, FieldValue::U32(0x00000042));
2310 }
2311
2312 #[test]
2313 fn parse_srv6_mobile_end_m_gtp6_e() {
2314 let sid: [u8; 16] = [
2318 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x47, 0x24, 0x12, 0x34, 0x56, 0x78, 0x00, ];
2324 let ss = SidStructure {
2325 locator_block_bits: 48,
2326 locator_node_bits: 16,
2327 function_bits: 16,
2328 argument_bits: 48,
2329 csid_flavor: CsidFlavor::Classic,
2330 mobile_encoding: Some(MobileSidEncoding::EndMGtp6E),
2331 };
2332 let dissector = Srv6Dissector::with_sid_structure(ss);
2333 let data = build_srh(6, 1, &[sid], 0, 0, &[]);
2334 let mut buf = DissectBuffer::new();
2335 dissector.dissect(&data, &mut buf, 0).unwrap();
2336
2337 let layer = &buf.layers()[0];
2338 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2339 let structure = array_entries(&buf, structure_field);
2340 let seg0 = structure[0];
2341
2342 assert!(nested_field_by_name(&buf, seg0, "locator_block").is_some());
2344 assert!(nested_field_by_name(&buf, seg0, "locator_node").is_some());
2345 assert!(nested_field_by_name(&buf, seg0, "function").is_some());
2346 assert!(nested_field_by_name(&buf, seg0, "argument").is_some());
2347
2348 assert!(nested_field_by_name(&buf, seg0, "args_mob_session").is_some());
2350
2351 assert!(nested_field_by_name(&buf, seg0, "embedded_ipv4").is_none());
2353 }
2354
2355 #[test]
2356 fn parse_srv6_mobile_h_m_gtp4_d() {
2357 let sid: [u8; 16] = [
2364 0xFD, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0xA8, 0x01, 0x01, 0x3E, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, ];
2369 let ss = SidStructure {
2370 locator_block_bits: 48,
2371 locator_node_bits: 0,
2372 function_bits: 0,
2373 argument_bits: 80,
2374 csid_flavor: CsidFlavor::Classic,
2375 mobile_encoding: Some(MobileSidEncoding::HmGtp4D {
2376 prefix_bits: 48,
2377 ipv4da_bits: 32,
2378 args_mob_session_bits: 40,
2379 }),
2380 };
2381 let dissector = Srv6Dissector::with_sid_structure(ss);
2382 let data = build_srh(6, 1, &[sid], 0, 0, &[]);
2383 let mut buf = DissectBuffer::new();
2384 dissector.dissect(&data, &mut buf, 0).unwrap();
2385
2386 let layer = &buf.layers()[0];
2387 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2388 let structure = array_entries(&buf, structure_field);
2389 let seg0 = structure[0];
2390
2391 let ipv4 = nested_field_by_name(&buf, seg0, "embedded_ipv4").unwrap();
2393 assert_eq!(ipv4.value, FieldValue::Ipv4Addr([192, 168, 1, 1]));
2394
2395 let ams = nested_field_by_name(&buf, seg0, "args_mob_session").unwrap();
2397 let ams_obj = buf.nested_fields(ams.value.as_container_range().unwrap());
2398 let qfi = ams_obj.iter().find(|f| f.name() == "qfi").unwrap();
2399 assert_eq!(qfi.value, FieldValue::U8(15));
2400 let r_flag = ams_obj.iter().find(|f| f.name() == "r_flag").unwrap();
2401 assert_eq!(r_flag.value, FieldValue::U8(1));
2402 let pdu_id = ams_obj
2403 .iter()
2404 .find(|f| f.name() == "pdu_session_id")
2405 .unwrap();
2406 assert_eq!(pdu_id.value, FieldValue::U32(0xDEADBEEF));
2407 }
2408
2409 #[test]
2410 fn parse_srv6_mobile_end_limit() {
2411 let sid: [u8; 16] = [
2414 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x29, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, ];
2418 let ss = SidStructure {
2419 locator_block_bits: 48,
2420 locator_node_bits: 0,
2421 function_bits: 16,
2422 argument_bits: 64,
2423 csid_flavor: CsidFlavor::Classic,
2424 mobile_encoding: Some(MobileSidEncoding::EndLimit {
2425 group_id_bits: 32,
2426 limit_rate_bits: 32,
2427 }),
2428 };
2429 let dissector = Srv6Dissector::with_sid_structure(ss);
2430 let data = build_srh(6, 1, &[sid], 0, 0, &[]);
2431 let mut buf = DissectBuffer::new();
2432 dissector.dissect(&data, &mut buf, 0).unwrap();
2433
2434 let layer = &buf.layers()[0];
2435 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2436 let structure = array_entries(&buf, structure_field);
2437 let seg0 = structure[0];
2438
2439 let gid = nested_field_by_name(&buf, seg0, "group_id").unwrap();
2441 assert_eq!(resolve_scratch(&buf, gid), &[0x00, 0x00, 0x00, 0x05]);
2442
2443 let lr = nested_field_by_name(&buf, seg0, "limit_rate").unwrap();
2445 assert_eq!(resolve_scratch(&buf, lr), &[0x00, 0x01, 0x00, 0x00]);
2446
2447 assert!(nested_field_by_name(&buf, seg0, "args_mob_session").is_none());
2449 assert!(nested_field_by_name(&buf, seg0, "embedded_ipv4").is_none());
2450 }
2451
2452 #[test]
2453 fn parse_srv6_mobile_encoding_none() {
2454 let ss = SidStructure {
2457 locator_block_bits: 48,
2458 locator_node_bits: 16,
2459 function_bits: 16,
2460 argument_bits: 48,
2461 csid_flavor: CsidFlavor::Classic,
2462 mobile_encoding: None,
2463 };
2464 let dissector = Srv6Dissector::with_sid_structure(ss);
2465 let data = build_srh(6, 1, &[SID_A], 0, 0, &[]);
2466 let mut buf = DissectBuffer::new();
2467 dissector.dissect(&data, &mut buf, 0).unwrap();
2468
2469 let layer = &buf.layers()[0];
2470 let structure_field = buf.field_by_name(layer, "segments_structure").unwrap();
2471 let structure = array_entries(&buf, structure_field);
2472 let seg0 = structure[0];
2473 let obj = buf.nested_fields(seg0.value.as_container_range().unwrap());
2474 assert_eq!(obj.len(), 4);
2476 assert!(nested_field_by_name(&buf, seg0, "args_mob_session").is_none());
2477 assert!(nested_field_by_name(&buf, seg0, "embedded_ipv4").is_none());
2478 assert!(nested_field_by_name(&buf, seg0, "group_id").is_none());
2479 assert!(nested_field_by_name(&buf, seg0, "limit_rate").is_none());
2480 }
2481
2482 fn call_format_fn(
2483 f: fn(&FieldValue<'_>, &FormatContext<'_>, &mut dyn std::io::Write) -> std::io::Result<()>,
2484 value: &FieldValue<'_>,
2485 scratch: &[u8],
2486 ) -> String {
2487 let ctx = FormatContext {
2488 packet_data: &[],
2489 scratch,
2490 layer_range: 0..0,
2491 field_range: 0..0,
2492 };
2493 let mut out = Vec::new();
2494 f(value, &ctx, &mut out).unwrap();
2495 String::from_utf8(out).unwrap()
2496 }
2497
2498 #[test]
2499 fn format_sid_hex_scratch() {
2500 let scratch = [0x20, 0x01, 0x0d, 0xb8];
2501 let val = FieldValue::Scratch(0..4);
2502 assert_eq!(
2503 call_format_fn(format_sid_hex, &val, &scratch),
2504 "\"20010db8\""
2505 );
2506 }
2507
2508 #[test]
2509 fn format_sid_hex_bytes() {
2510 let data = [0xab, 0xcd, 0xef];
2511 let val = FieldValue::Bytes(&data);
2512 assert_eq!(call_format_fn(format_sid_hex, &val, &[]), "\"abcdef\"");
2513 }
2514
2515 #[test]
2516 fn format_sid_hex_empty_scratch() {
2517 let val = FieldValue::Scratch(0..0);
2518 assert_eq!(call_format_fn(format_sid_hex, &val, &[]), "\"\"");
2519 }
2520
2521 #[test]
2522 fn format_sid_hex_empty_bytes() {
2523 let val = FieldValue::Bytes(&[]);
2524 assert_eq!(call_format_fn(format_sid_hex, &val, &[]), "\"\"");
2525 }
2526
2527 #[test]
2528 fn format_sid_hex_other_variant() {
2529 let val = FieldValue::U8(42);
2530 assert_eq!(call_format_fn(format_sid_hex, &val, &[]), "\"\"");
2531 }
2532
2533 #[test]
2534 fn tlv_container_resolves_to_tlv_name() {
2535 let tlvs = [4, 4, 0, 0, 0, 0];
2538 let data = build_srh(6, 1, &[SID_A], 0, 0, &tlvs);
2539 let mut buf = DissectBuffer::new();
2540 Srv6Dissector::new().dissect(&data, &mut buf, 0).unwrap();
2541
2542 let (idx, field) = buf
2543 .fields()
2544 .iter()
2545 .enumerate()
2546 .find(|(_, f)| f.name() == "tlv")
2547 .expect("tlv container not found");
2548 assert!(matches!(field.value, FieldValue::Object(_)));
2549 assert_eq!(field.display_name(), "TLV");
2550 assert_eq!(buf.resolve_container_display_name(idx as u32), Some("PadN"));
2551 }
2552}