1use compact_str::CompactString;
11use smallvec::SmallVec;
12
13use super::ethernet::ethertype;
14use super::{FieldValue, ParseContext, ParseResult, Protocol, TunnelType};
15use crate::schema::{DataKind, FieldDescriptor};
16
17pub const GTP_U_PORT: u16 = 2152;
19
20pub const GTP_C_PORT: u16 = 2123;
22
23const MAX_EXTENSION_HEADERS: usize = 16;
25
26#[allow(dead_code)]
28pub mod message_type {
29 pub const ECHO_REQUEST: u8 = 1;
30 pub const ECHO_RESPONSE: u8 = 2;
31 pub const ERROR_INDICATION: u8 = 26;
32 pub const SUPPORTED_EXTENSION_HEADERS_NOTIFICATION: u8 = 31;
33 pub const END_MARKER: u8 = 254;
34 pub const G_PDU: u8 = 255;
35}
36
37pub mod extension_header_type {
39 pub const NO_MORE: u8 = 0x00;
40 pub const MBMS_SUPPORT_INDICATION: u8 = 0x01;
41 pub const MS_INFO_CHANGE_REPORTING: u8 = 0x02;
42 pub const SERVICE_CLASS_INDICATOR: u8 = 0x20;
43 pub const UDP_PORT: u8 = 0x40;
44 pub const RAN_CONTAINER: u8 = 0x81;
45 pub const LONG_PDCP_PDU_NUMBER: u8 = 0x82;
46 pub const XW_RAN_CONTAINER: u8 = 0x83;
47 pub const NR_RAN_CONTAINER: u8 = 0x84;
48 pub const PDU_SESSION_CONTAINER: u8 = 0x85;
49 pub const PDCP_PDU_NUMBER: u8 = 0xC0;
50}
51
52fn extension_header_type_name(ext_type: u8) -> &'static str {
54 match ext_type {
55 extension_header_type::NO_MORE => "No More",
56 extension_header_type::MBMS_SUPPORT_INDICATION => "MBMS Support Indication",
57 extension_header_type::MS_INFO_CHANGE_REPORTING => "MS Info Change Reporting",
58 extension_header_type::SERVICE_CLASS_INDICATOR => "Service Class Indicator",
59 extension_header_type::UDP_PORT => "UDP Port",
60 extension_header_type::RAN_CONTAINER => "RAN Container",
61 extension_header_type::LONG_PDCP_PDU_NUMBER => "Long PDCP PDU Number",
62 extension_header_type::XW_RAN_CONTAINER => "Xw RAN Container",
63 extension_header_type::NR_RAN_CONTAINER => "NR RAN Container",
64 extension_header_type::PDU_SESSION_CONTAINER => "PDU Session Container",
65 extension_header_type::PDCP_PDU_NUMBER => "PDCP PDU Number",
66 _ => "Unknown",
67 }
68}
69
70#[derive(Debug, Clone, Copy)]
72pub struct GtpProtocol;
73
74impl Protocol for GtpProtocol {
75 fn name(&self) -> &'static str {
76 "gtp"
77 }
78
79 fn display_name(&self) -> &'static str {
80 "GTP"
81 }
82
83 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
84 match context.hint("dst_port") {
86 Some(port) if port == GTP_U_PORT as u64 => Some(100),
87 Some(port) if port == GTP_C_PORT as u64 => Some(100),
88 _ => None,
89 }
90 }
91
92 fn parse<'a>(&self, data: &'a [u8], context: &ParseContext) -> ParseResult<'a> {
93 if data.len() < 8 {
95 return ParseResult::error("GTP header too short".to_string(), data);
96 }
97
98 let mut fields = SmallVec::new();
99
100 let flags = data[0];
108 let version = (flags >> 5) & 0x07;
109 fields.push(("version", FieldValue::UInt8(version)));
110
111 if version == 2 {
113 return self.parse_gtpv2(data, context);
114 }
115
116 let protocol_type = (flags >> 4) & 0x01;
118 let extension_header_flag = (flags >> 2) & 0x01 == 1;
119 let sequence_flag = (flags >> 1) & 0x01 == 1;
120 let npdu_flag = flags & 0x01 == 1;
121
122 fields.push(("protocol_type", FieldValue::UInt8(protocol_type)));
123
124 let message_type = data[1];
126 fields.push(("message_type", FieldValue::UInt8(message_type)));
127
128 let length = u16::from_be_bytes([data[2], data[3]]);
130 fields.push(("length", FieldValue::UInt16(length)));
131
132 let teid = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
134 fields.push(("teid", FieldValue::UInt32(teid)));
135
136 let mut offset = 8;
137
138 if extension_header_flag || sequence_flag || npdu_flag {
140 if data.len() < offset + 4 {
141 return ParseResult::error("GTP: optional header fields missing".to_string(), data);
142 }
143
144 if sequence_flag {
146 let sequence = u16::from_be_bytes([data[offset], data[offset + 1]]);
147 fields.push(("sequence", FieldValue::UInt16(sequence)));
148 }
149 offset += 2;
150
151 if npdu_flag {
153 fields.push(("npdu", FieldValue::UInt8(data[offset])));
154 }
155 offset += 1;
156
157 let mut next_ext_type = data[offset];
159 offset += 1;
160
161 if extension_header_flag && next_ext_type != 0 {
163 let mut ext_headers = Vec::new();
164 let mut ext_count = 0u8;
165
166 while next_ext_type != extension_header_type::NO_MORE
167 && offset < data.len()
168 && ext_count < MAX_EXTENSION_HEADERS as u8
169 {
170 if offset >= data.len() {
172 break;
173 }
174 let ext_len_units = data[offset] as usize;
175 let ext_len = ext_len_units * 4;
176
177 if ext_len == 0 || offset + ext_len > data.len() {
178 break;
180 }
181
182 ext_headers.push(format!(
184 "{}(0x{:02X})",
185 extension_header_type_name(next_ext_type),
186 next_ext_type
187 ));
188
189 next_ext_type = data[offset + ext_len - 1];
191 offset += ext_len;
192 ext_count += 1;
193 }
194
195 if !ext_headers.is_empty() {
196 fields.push((
197 "extension_headers",
198 FieldValue::OwnedString(CompactString::new(ext_headers.join(","))),
199 ));
200 fields.push(("extension_header_count", FieldValue::UInt8(ext_count)));
201 }
202 }
203 }
204
205 let mut child_hints = SmallVec::new();
207
208 if message_type == message_type::G_PDU && offset < data.len() {
210 let first_byte = data[offset];
211 let ip_version = (first_byte >> 4) & 0x0F;
212
213 match ip_version {
214 4 => {
215 child_hints.push(("ethertype", ethertype::IPV4 as u64));
216 child_hints.push(("ip_version", 4u64));
217 }
218 6 => {
219 child_hints.push(("ethertype", ethertype::IPV6 as u64));
220 child_hints.push(("ip_version", 6u64));
221 }
222 _ => {}
223 }
224 }
225
226 if let Some(port) = context.hint("dst_port") {
228 if port == GTP_U_PORT as u64 {
229 child_hints.push(("gtp_plane", 1u64)); } else if port == GTP_C_PORT as u64 {
231 child_hints.push(("gtp_plane", 0u64)); }
233 }
234
235 child_hints.push(("tunnel_type", TunnelType::Gtp as u64));
237 child_hints.push(("tunnel_id", teid as u64)); ParseResult::success(fields, &data[offset..], child_hints)
240 }
241
242 fn schema_fields(&self) -> Vec<FieldDescriptor> {
243 vec![
244 FieldDescriptor::new("gtp.version", DataKind::UInt8).set_nullable(true),
246 FieldDescriptor::new("gtp.message_type", DataKind::UInt8).set_nullable(true),
247 FieldDescriptor::new("gtp.length", DataKind::UInt16).set_nullable(true),
248 FieldDescriptor::new("gtp.teid", DataKind::UInt32).set_nullable(true),
249 FieldDescriptor::new("gtp.protocol_type", DataKind::UInt8).set_nullable(true),
251 FieldDescriptor::new("gtp.sequence", DataKind::UInt16).set_nullable(true),
252 FieldDescriptor::new("gtp.npdu", DataKind::UInt8).set_nullable(true),
253 FieldDescriptor::new("gtp.extension_headers", DataKind::String).set_nullable(true),
254 FieldDescriptor::new("gtp.extension_header_count", DataKind::UInt8).set_nullable(true),
255 FieldDescriptor::new("gtp.piggyback", DataKind::Bool).set_nullable(true),
257 FieldDescriptor::new("gtp.teid_present", DataKind::Bool).set_nullable(true),
258 ]
259 }
260
261 fn child_protocols(&self) -> &[&'static str] {
262 &["ipv4", "ipv6"]
264 }
265
266 fn dependencies(&self) -> &'static [&'static str] {
267 &["udp"] }
269}
270
271impl GtpProtocol {
272 fn parse_gtpv2<'a>(&self, data: &'a [u8], context: &ParseContext) -> ParseResult<'a> {
289 if data.len() < 8 {
290 return ParseResult::error("GTPv2 header too short".to_string(), data);
291 }
292
293 let mut fields = SmallVec::new();
294
295 let flags = data[0];
296 let version = (flags >> 5) & 0x07;
297 let piggyback = (flags >> 4) & 0x01 == 1;
298 let teid_present = (flags >> 3) & 0x01 == 1;
299
300 fields.push(("version", FieldValue::UInt8(version)));
301 fields.push(("piggyback", FieldValue::Bool(piggyback)));
302 fields.push(("teid_present", FieldValue::Bool(teid_present)));
303
304 let message_type = data[1];
306 fields.push(("message_type", FieldValue::UInt8(message_type)));
307
308 let length = u16::from_be_bytes([data[2], data[3]]);
310 fields.push(("length", FieldValue::UInt16(length)));
311
312 let mut offset = 4;
313
314 if teid_present {
316 if data.len() < offset + 4 {
317 return ParseResult::error("GTPv2: missing TEID field".to_string(), data);
318 }
319 let teid = u32::from_be_bytes([
320 data[offset],
321 data[offset + 1],
322 data[offset + 2],
323 data[offset + 3],
324 ]);
325 fields.push(("teid", FieldValue::UInt32(teid)));
326 offset += 4;
327 }
328
329 if data.len() < offset + 4 {
331 return ParseResult::error("GTPv2: missing sequence number".to_string(), data);
332 }
333 let sequence = ((data[offset] as u32) << 16)
334 | ((data[offset + 1] as u32) << 8)
335 | (data[offset + 2] as u32);
336 fields.push(("sequence", FieldValue::UInt32(sequence)));
337 offset += 4;
338
339 let mut child_hints = SmallVec::new();
341 if let Some(port) = context.hint("dst_port") {
342 if port == GTP_C_PORT as u64 {
343 child_hints.push(("gtp_plane", 0u64)); }
345 }
346
347 ParseResult::success(fields, &data[offset..], child_hints)
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 fn create_gtp_header(
357 message_type: u8,
358 teid: u32,
359 payload_len: u16,
360 sequence: Option<u16>,
361 ) -> Vec<u8> {
362 let mut header = Vec::new();
363
364 let mut flags = 0x30u8; if sequence.is_some() {
367 flags |= 0x02; }
369 header.push(flags);
370
371 header.push(message_type);
373
374 let len = if sequence.is_some() {
376 payload_len + 4 } else {
378 payload_len
379 };
380 header.extend_from_slice(&len.to_be_bytes());
381
382 header.extend_from_slice(&teid.to_be_bytes());
384
385 if let Some(seq) = sequence {
387 header.extend_from_slice(&seq.to_be_bytes());
388 header.push(0); header.push(0); }
391
392 header
393 }
394
395 #[test]
397 fn test_can_parse_with_gtp_u_port() {
398 let parser = GtpProtocol;
399
400 let ctx1 = ParseContext::new(1);
402 assert!(parser.can_parse(&ctx1).is_none());
403
404 let mut ctx2 = ParseContext::new(1);
406 ctx2.insert_hint("dst_port", 80);
407 assert!(parser.can_parse(&ctx2).is_none());
408
409 let mut ctx3 = ParseContext::new(1);
411 ctx3.insert_hint("dst_port", 2152);
412 assert!(parser.can_parse(&ctx3).is_some());
413 assert_eq!(parser.can_parse(&ctx3), Some(100));
414 }
415
416 #[test]
418 fn test_can_parse_with_gtp_c_port() {
419 let parser = GtpProtocol;
420
421 let mut context = ParseContext::new(1);
422 context.insert_hint("dst_port", 2123);
423
424 assert!(parser.can_parse(&context).is_some());
425 assert_eq!(parser.can_parse(&context), Some(100));
426 }
427
428 #[test]
430 fn test_gtpv1_header_parsing() {
431 let header = create_gtp_header(message_type::G_PDU, 0x12345678, 0, None);
432
433 let parser = GtpProtocol;
434 let mut context = ParseContext::new(1);
435 context.insert_hint("dst_port", 2152);
436
437 let result = parser.parse(&header, &context);
438
439 assert!(result.is_ok());
440 assert_eq!(result.get("version"), Some(&FieldValue::UInt8(1)));
441 assert_eq!(result.get("protocol_type"), Some(&FieldValue::UInt8(1)));
442 assert_eq!(result.get("message_type"), Some(&FieldValue::UInt8(255)));
443 }
444
445 #[test]
447 fn test_teid_extraction() {
448 let parser = GtpProtocol;
449 let mut context = ParseContext::new(1);
450 context.insert_hint("dst_port", 2152);
451
452 let test_teids = [0u32, 1, 0x12345678, 0xFFFFFFFF];
454
455 for teid in test_teids {
456 let header = create_gtp_header(message_type::G_PDU, teid, 0, None);
457 let result = parser.parse(&header, &context);
458
459 assert!(result.is_ok());
460 assert_eq!(result.get("teid"), Some(&FieldValue::UInt32(teid)));
461 }
462 }
463
464 #[test]
466 fn test_message_type_parsing() {
467 let parser = GtpProtocol;
468 let mut context = ParseContext::new(1);
469 context.insert_hint("dst_port", 2152);
470
471 let test_types = [
472 message_type::ECHO_REQUEST,
473 message_type::ECHO_RESPONSE,
474 message_type::ERROR_INDICATION,
475 message_type::G_PDU,
476 ];
477
478 for msg_type in test_types {
479 let header = create_gtp_header(msg_type, 0x1234, 0, None);
480 let result = parser.parse(&header, &context);
481
482 assert!(result.is_ok());
483 assert_eq!(
484 result.get("message_type"),
485 Some(&FieldValue::UInt8(msg_type))
486 );
487 }
488 }
489
490 #[test]
492 fn test_optional_sequence_number() {
493 let parser = GtpProtocol;
494 let mut context = ParseContext::new(1);
495 context.insert_hint("dst_port", 2152);
496
497 let header = create_gtp_header(message_type::G_PDU, 0x1234, 0, Some(0xABCD));
499 let result = parser.parse(&header, &context);
500
501 assert!(result.is_ok());
502 assert_eq!(result.get("sequence"), Some(&FieldValue::UInt16(0xABCD)));
503 }
504
505 #[test]
507 fn test_gpdu_child_protocol_detection() {
508 let parser = GtpProtocol;
509 let mut context = ParseContext::new(1);
510 context.insert_hint("dst_port", 2152);
511
512 let mut data_ipv4 = create_gtp_header(message_type::G_PDU, 0x1234, 20, None);
514 data_ipv4.extend_from_slice(&[0x45, 0x00, 0x00, 0x14]); let result_ipv4 = parser.parse(&data_ipv4, &context);
517 assert!(result_ipv4.is_ok());
518 assert_eq!(result_ipv4.hint("ethertype"), Some(0x0800u64));
519 assert_eq!(result_ipv4.hint("ip_version"), Some(4u64));
520
521 let mut data_ipv6 = create_gtp_header(message_type::G_PDU, 0x1234, 40, None);
523 data_ipv6.extend_from_slice(&[0x60, 0x00, 0x00, 0x00]); let result_ipv6 = parser.parse(&data_ipv6, &context);
526 assert!(result_ipv6.is_ok());
527 assert_eq!(result_ipv6.hint("ethertype"), Some(0x86DDu64));
528 assert_eq!(result_ipv6.hint("ip_version"), Some(6u64));
529 }
530
531 #[test]
533 fn test_gtp_too_short() {
534 let parser = GtpProtocol;
535 let mut context = ParseContext::new(1);
536 context.insert_hint("dst_port", 2152);
537
538 let short_header = [0x30, 0xFF, 0x00, 0x00]; let result = parser.parse(&short_header, &context);
540
541 assert!(!result.is_ok());
542 assert!(result.error.is_some());
543 }
544
545 #[test]
547 fn test_length_field() {
548 let parser = GtpProtocol;
549 let mut context = ParseContext::new(1);
550 context.insert_hint("dst_port", 2152);
551
552 let mut header = create_gtp_header(message_type::G_PDU, 0x1234, 100, None);
553 header.extend(vec![0u8; 100]);
555
556 let result = parser.parse(&header, &context);
557
558 assert!(result.is_ok());
559 assert_eq!(result.get("length"), Some(&FieldValue::UInt16(100)));
560 assert_eq!(result.remaining.len(), 100);
561 }
562
563 #[test]
565 fn test_gtp_schema_fields() {
566 let parser = GtpProtocol;
567 let fields = parser.schema_fields();
568
569 assert!(!fields.is_empty());
570 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
571 assert!(field_names.contains(&"gtp.version"));
572 assert!(field_names.contains(&"gtp.protocol_type"));
573 assert!(field_names.contains(&"gtp.message_type"));
574 assert!(field_names.contains(&"gtp.length"));
575 assert!(field_names.contains(&"gtp.teid"));
576 assert!(field_names.contains(&"gtp.sequence"));
577 }
578
579 #[test]
581 fn test_gtp_prime() {
582 let parser = GtpProtocol;
583 let mut context = ParseContext::new(1);
584 context.insert_hint("dst_port", 2152);
585
586 let header = vec![
588 0x20, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, ];
593
594 let result = parser.parse(&header, &context);
595
596 assert!(result.is_ok());
597 assert_eq!(result.get("protocol_type"), Some(&FieldValue::UInt8(0)));
598 }
599
600 #[test]
602 fn test_extension_header_single() {
603 let parser = GtpProtocol;
604 let mut context = ParseContext::new(1);
605 context.insert_hint("dst_port", 2152);
606
607 let header = vec![
609 0x34, message_type::G_PDU,
611 0x00,
612 0x0C, 0x00,
614 0x00,
615 0x00,
616 0x01, 0x00,
618 0x00, 0x00, extension_header_type::PDCP_PDU_NUMBER, 0x01, 0x12,
624 0x34, extension_header_type::NO_MORE, ];
627
628 let result = parser.parse(&header, &context);
629
630 assert!(result.is_ok());
631 assert_eq!(
632 result.get("extension_header_count"),
633 Some(&FieldValue::UInt8(1))
634 );
635
636 if let Some(FieldValue::OwnedString(ext)) = result.get("extension_headers") {
637 assert!(ext.contains("PDCP PDU Number"));
638 assert!(ext.contains("0xC0"));
639 } else {
640 panic!("Expected extension_headers field");
641 }
642 }
643
644 #[test]
646 fn test_extension_header_chain() {
647 let parser = GtpProtocol;
648 let mut context = ParseContext::new(1);
649 context.insert_hint("dst_port", 2152);
650
651 let header = vec![
653 0x34, message_type::G_PDU,
655 0x00,
656 0x14, 0x00,
658 0x00,
659 0x00,
660 0x01, 0x00,
662 0x00, 0x00, extension_header_type::UDP_PORT, 0x01, 0x08,
668 0x68, extension_header_type::PDU_SESSION_CONTAINER, 0x01, 0x01,
673 0x00, extension_header_type::NO_MORE, ];
676
677 let result = parser.parse(&header, &context);
678
679 assert!(result.is_ok());
680 assert_eq!(
681 result.get("extension_header_count"),
682 Some(&FieldValue::UInt8(2))
683 );
684
685 if let Some(FieldValue::OwnedString(ext)) = result.get("extension_headers") {
686 assert!(ext.contains("UDP Port"));
687 assert!(ext.contains("PDU Session Container"));
688 } else {
689 panic!("Expected extension_headers field");
690 }
691 }
692
693 #[test]
695 fn test_extension_header_max_limit() {
696 let parser = GtpProtocol;
697 let mut context = ParseContext::new(1);
698 context.insert_hint("dst_port", 2152);
699
700 let mut header = vec![
702 0x34, message_type::G_PDU,
704 0x00,
705 0x60, 0x00,
707 0x00,
708 0x00,
709 0x01, 0x00,
711 0x00, 0x00, extension_header_type::UDP_PORT, ];
715
716 for i in 0..20 {
718 header.push(0x01); header.push((i & 0xFF) as u8); header.push(((i >> 8) & 0xFF) as u8); if i < 19 {
722 header.push(extension_header_type::UDP_PORT); } else {
724 header.push(extension_header_type::NO_MORE); }
726 }
727
728 let result = parser.parse(&header, &context);
729
730 assert!(result.is_ok());
731 if let Some(FieldValue::UInt8(count)) = result.get("extension_header_count") {
733 assert!(
734 *count <= 16,
735 "Extension header count should be capped at 16"
736 );
737 }
738 }
739
740 #[test]
742 fn test_gtpv2_basic_parsing() {
743 let parser = GtpProtocol;
744 let mut context = ParseContext::new(1);
745 context.insert_hint("dst_port", 2123);
746
747 let header = vec![
749 0x48, 0x20, 0x00, 0x0C, 0xAB, 0xCD, 0xEF, 0x12, 0x00, 0x01, 0x23, 0x00, ];
756
757 let result = parser.parse(&header, &context);
758
759 assert!(result.is_ok());
760 assert_eq!(result.get("version"), Some(&FieldValue::UInt8(2)));
761 assert_eq!(result.get("teid_present"), Some(&FieldValue::Bool(true)));
762 assert_eq!(result.get("teid"), Some(&FieldValue::UInt32(0xABCDEF12)));
763 assert_eq!(result.get("message_type"), Some(&FieldValue::UInt8(0x20)));
764 }
765
766 #[test]
768 fn test_gtpv2_no_teid() {
769 let parser = GtpProtocol;
770 let mut context = ParseContext::new(1);
771 context.insert_hint("dst_port", 2123);
772
773 let header = vec![
775 0x40, 0x01, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, ];
781
782 let result = parser.parse(&header, &context);
783
784 assert!(result.is_ok());
785 assert_eq!(result.get("version"), Some(&FieldValue::UInt8(2)));
786 assert_eq!(result.get("teid_present"), Some(&FieldValue::Bool(false)));
787 assert!(result.get("teid").is_none()); }
789
790 #[test]
792 fn test_gtpv2_piggyback() {
793 let parser = GtpProtocol;
794 let mut context = ParseContext::new(1);
795 context.insert_hint("dst_port", 2123);
796
797 let header = vec![
799 0x58, 0x21, 0x00, 0x0C, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x01, 0x00, ];
806
807 let result = parser.parse(&header, &context);
808
809 assert!(result.is_ok());
810 assert_eq!(result.get("piggyback"), Some(&FieldValue::Bool(true)));
811 }
812
813 #[test]
815 fn test_gtpv2_sequence_number() {
816 let parser = GtpProtocol;
817 let mut context = ParseContext::new(1);
818 context.insert_hint("dst_port", 2123);
819
820 let header = vec![
822 0x48, 0x22, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0xAB, 0xCD, 0xEF, 0x00, ];
829
830 let result = parser.parse(&header, &context);
831
832 assert!(result.is_ok());
833 assert_eq!(result.get("sequence"), Some(&FieldValue::UInt32(0xABCDEF)));
835 }
836
837 #[test]
839 fn test_gtpv1_npdu_flag() {
840 let parser = GtpProtocol;
841 let mut context = ParseContext::new(1);
842 context.insert_hint("dst_port", 2152);
843
844 let header = vec![
846 0x31, message_type::G_PDU,
848 0x00,
849 0x08, 0x00,
851 0x00,
852 0x00,
853 0x01, 0x00,
855 0x00, 0x42, 0x00, ];
859
860 let result = parser.parse(&header, &context);
861
862 assert!(result.is_ok());
863 assert_eq!(result.get("npdu"), Some(&FieldValue::UInt8(0x42)));
864 }
865
866 #[test]
868 fn test_extension_header_type_names() {
869 let parser = GtpProtocol;
871 let mut context = ParseContext::new(1);
872 context.insert_hint("dst_port", 2152);
873
874 let test_cases = [
875 (extension_header_type::RAN_CONTAINER, "RAN Container"),
876 (extension_header_type::NR_RAN_CONTAINER, "NR RAN Container"),
877 (
878 extension_header_type::LONG_PDCP_PDU_NUMBER,
879 "Long PDCP PDU Number",
880 ),
881 ];
882
883 for (ext_type, expected_name) in test_cases {
884 let header = vec![
885 0x34, message_type::G_PDU,
887 0x00,
888 0x0C, 0x00,
890 0x00,
891 0x00,
892 0x01, 0x00,
894 0x00, 0x00, ext_type, 0x01, 0x00,
899 0x00, extension_header_type::NO_MORE, ];
902
903 let result = parser.parse(&header, &context);
904 assert!(result.is_ok());
905
906 if let Some(FieldValue::OwnedString(ext)) = result.get("extension_headers") {
907 assert!(
908 ext.contains(expected_name),
909 "Expected '{}' in '{}'",
910 expected_name,
911 ext
912 );
913 } else {
914 panic!("Expected extension_headers field");
915 }
916 }
917 }
918
919 #[test]
921 fn test_gtpv2_message_types() {
922 let parser = GtpProtocol;
923 let mut context = ParseContext::new(1);
924 context.insert_hint("dst_port", 2123);
925
926 let test_types: [(u8, &str); 6] = [
928 (1, "Echo Request"),
929 (2, "Echo Response"),
930 (32, "Create Session Request"),
931 (33, "Create Session Response"),
932 (34, "Modify Bearer Request"),
933 (35, "Modify Bearer Response"),
934 ];
935
936 for (msg_type, _name) in test_types {
937 let header = vec![
938 0x48, msg_type, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, ];
944
945 let result = parser.parse(&header, &context);
946 assert!(result.is_ok());
947 assert_eq!(
948 result.get("message_type"),
949 Some(&FieldValue::UInt8(msg_type))
950 );
951 }
952 }
953
954 #[test]
956 fn test_gtpv1_all_optional_flags() {
957 let parser = GtpProtocol;
958 let mut context = ParseContext::new(1);
959 context.insert_hint("dst_port", 2152);
960
961 let header = vec![
963 0x37, message_type::G_PDU,
965 0x00,
966 0x10, 0x12,
968 0x34,
969 0x56,
970 0x78, 0xAB,
972 0xCD, 0x42, extension_header_type::UDP_PORT, 0x01, 0x08,
977 0x68, extension_header_type::NO_MORE,
979 ];
980
981 let result = parser.parse(&header, &context);
982
983 assert!(result.is_ok());
984 assert_eq!(result.get("teid"), Some(&FieldValue::UInt32(0x12345678)));
985 assert_eq!(result.get("sequence"), Some(&FieldValue::UInt16(0xABCD)));
986 assert_eq!(result.get("npdu"), Some(&FieldValue::UInt8(0x42)));
987 assert_eq!(
988 result.get("extension_header_count"),
989 Some(&FieldValue::UInt8(1))
990 );
991 }
992}