1use std::fmt::{self, Formatter};
78
79use zerocopy::byteorder::{BigEndian, U16, U32};
80use zerocopy::{FromBytes, IntoBytes, Unaligned};
81
82use crate::packet::{HeaderParser, PacketHeader};
83
84pub const GTPV1_C_PORT: u16 = 2123;
86
87pub const GTPV1_U_PORT: u16 = 2152;
89
90#[inline]
92pub fn is_gtpv1_c_port(port: u16) -> bool {
93 port == GTPV1_C_PORT
94}
95
96#[inline]
98pub fn is_gtpv1_u_port(port: u16) -> bool {
99 port == GTPV1_U_PORT
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104#[repr(u8)]
105pub enum Gtpv1MessageType {
106 EchoRequest = 1,
108 EchoResponse = 2,
109 VersionNotSupported = 3,
110
111 CreatePdpContextRequest = 16,
113 CreatePdpContextResponse = 17,
114 UpdatePdpContextRequest = 18,
115 UpdatePdpContextResponse = 19,
116 DeletePdpContextRequest = 20,
117 DeletePdpContextResponse = 21,
118
119 SgsnContextRequest = 50,
121 SgsnContextResponse = 51,
122 SgsnContextAcknowledge = 52,
123
124 SendRoutingInfoRequest = 32,
126 SendRoutingInfoResponse = 33,
127
128 MbmsNotificationRequest = 96,
130 MbmsNotificationResponse = 97,
131 MbmsNotificationRejectRequest = 98,
132 MbmsNotificationRejectResponse = 99,
133
134 ErrorIndication = 26,
136 SupportedExtensionHeadersNotification = 31,
137 EndMarker = 254,
138 GPdu = 255,
139
140 Unknown = 0,
142}
143
144impl From<u8> for Gtpv1MessageType {
145 fn from(value: u8) -> Self {
146 match value {
147 1 => Gtpv1MessageType::EchoRequest,
148 2 => Gtpv1MessageType::EchoResponse,
149 3 => Gtpv1MessageType::VersionNotSupported,
150 16 => Gtpv1MessageType::CreatePdpContextRequest,
151 17 => Gtpv1MessageType::CreatePdpContextResponse,
152 18 => Gtpv1MessageType::UpdatePdpContextRequest,
153 19 => Gtpv1MessageType::UpdatePdpContextResponse,
154 20 => Gtpv1MessageType::DeletePdpContextRequest,
155 21 => Gtpv1MessageType::DeletePdpContextResponse,
156 26 => Gtpv1MessageType::ErrorIndication,
157 31 => Gtpv1MessageType::SupportedExtensionHeadersNotification,
158 32 => Gtpv1MessageType::SendRoutingInfoRequest,
159 33 => Gtpv1MessageType::SendRoutingInfoResponse,
160 50 => Gtpv1MessageType::SgsnContextRequest,
161 51 => Gtpv1MessageType::SgsnContextResponse,
162 52 => Gtpv1MessageType::SgsnContextAcknowledge,
163 96 => Gtpv1MessageType::MbmsNotificationRequest,
164 97 => Gtpv1MessageType::MbmsNotificationResponse,
165 98 => Gtpv1MessageType::MbmsNotificationRejectRequest,
166 99 => Gtpv1MessageType::MbmsNotificationRejectResponse,
167 254 => Gtpv1MessageType::EndMarker,
168 255 => Gtpv1MessageType::GPdu,
169 _ => Gtpv1MessageType::Unknown,
170 }
171 }
172}
173
174impl fmt::Display for Gtpv1MessageType {
175 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
176 match self {
177 Gtpv1MessageType::EchoRequest => write!(f, "Echo Request"),
178 Gtpv1MessageType::EchoResponse => write!(f, "Echo Response"),
179 Gtpv1MessageType::VersionNotSupported => write!(f, "Version Not Supported"),
180 Gtpv1MessageType::CreatePdpContextRequest => write!(f, "Create PDP Context Request"),
181 Gtpv1MessageType::CreatePdpContextResponse => write!(f, "Create PDP Context Response"),
182 Gtpv1MessageType::UpdatePdpContextRequest => write!(f, "Update PDP Context Request"),
183 Gtpv1MessageType::UpdatePdpContextResponse => write!(f, "Update PDP Context Response"),
184 Gtpv1MessageType::DeletePdpContextRequest => write!(f, "Delete PDP Context Request"),
185 Gtpv1MessageType::DeletePdpContextResponse => write!(f, "Delete PDP Context Response"),
186 Gtpv1MessageType::ErrorIndication => write!(f, "Error Indication"),
187 Gtpv1MessageType::SupportedExtensionHeadersNotification => {
188 write!(f, "Supported Extension Headers Notification")
189 }
190 Gtpv1MessageType::SendRoutingInfoRequest => write!(f, "Send Routing Info Request"),
191 Gtpv1MessageType::SendRoutingInfoResponse => write!(f, "Send Routing Info Response"),
192 Gtpv1MessageType::SgsnContextRequest => write!(f, "SGSN Context Request"),
193 Gtpv1MessageType::SgsnContextResponse => write!(f, "SGSN Context Response"),
194 Gtpv1MessageType::SgsnContextAcknowledge => write!(f, "SGSN Context Acknowledge"),
195 Gtpv1MessageType::MbmsNotificationRequest => write!(f, "MBMS Notification Request"),
196 Gtpv1MessageType::MbmsNotificationResponse => write!(f, "MBMS Notification Response"),
197 Gtpv1MessageType::MbmsNotificationRejectRequest => {
198 write!(f, "MBMS Notification Reject Request")
199 }
200 Gtpv1MessageType::MbmsNotificationRejectResponse => {
201 write!(f, "MBMS Notification Reject Response")
202 }
203 Gtpv1MessageType::EndMarker => write!(f, "End Marker"),
204 Gtpv1MessageType::GPdu => write!(f, "G-PDU"),
205 Gtpv1MessageType::Unknown => write!(f, "Unknown"),
206 }
207 }
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212#[repr(u8)]
213pub enum Gtpv1ExtensionType {
214 NoMoreExtensions = 0x00,
216 MbmsSupportIndication = 0x01,
218 MsInfoChangeReporting = 0x02,
220 LongPdcpPduNumber = 0x03,
222 ServiceClassIndicator = 0x20,
224 UdpPort = 0x40,
226 RanContainer = 0x81,
228 LongPdcpPduNumberExt = 0x82,
230 XwRanContainer = 0x83,
232 NrRanContainer = 0x84,
234 PduSessionContainer = 0x85,
236 PdcpPduNumber = 0xC0,
238 Unknown = 0xFF,
240}
241
242impl From<u8> for Gtpv1ExtensionType {
243 fn from(value: u8) -> Self {
244 match value {
245 0x00 => Gtpv1ExtensionType::NoMoreExtensions,
246 0x01 => Gtpv1ExtensionType::MbmsSupportIndication,
247 0x02 => Gtpv1ExtensionType::MsInfoChangeReporting,
248 0x03 => Gtpv1ExtensionType::LongPdcpPduNumber,
249 0x20 => Gtpv1ExtensionType::ServiceClassIndicator,
250 0x40 => Gtpv1ExtensionType::UdpPort,
251 0x81 => Gtpv1ExtensionType::RanContainer,
252 0x82 => Gtpv1ExtensionType::LongPdcpPduNumberExt,
253 0x83 => Gtpv1ExtensionType::XwRanContainer,
254 0x84 => Gtpv1ExtensionType::NrRanContainer,
255 0x85 => Gtpv1ExtensionType::PduSessionContainer,
256 0xC0 => Gtpv1ExtensionType::PdcpPduNumber,
257 _ => Gtpv1ExtensionType::Unknown,
258 }
259 }
260}
261
262#[repr(C, packed)]
268#[derive(
269 FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
270)]
271pub struct Gtpv1Header {
272 flags: u8,
273 message_type: u8,
274 length: U16<BigEndian>,
275 teid: U32<BigEndian>,
276}
277
278impl Gtpv1Header {
279 pub const VERSION_MASK: u8 = 0xE0;
281 pub const VERSION_SHIFT: u8 = 5;
282
283 pub const FLAG_PT: u8 = 0x10;
285
286 pub const FLAG_RESERVED: u8 = 0x08;
288
289 pub const FLAG_E: u8 = 0x04;
291
292 pub const FLAG_S: u8 = 0x02;
294
295 pub const FLAG_PN: u8 = 0x01;
297
298 pub const VERSION_1: u8 = 1;
300
301 pub const MIN_HEADER_LEN: usize = 8;
303
304 pub const OPTIONAL_HEADER_LEN: usize = 12;
306
307 #[allow(unused)]
308 const NAME: &'static str = "Gtpv1Header";
309
310 #[inline]
312 pub fn flags(&self) -> u8 {
313 self.flags
314 }
315
316 #[inline]
318 pub fn version(&self) -> u8 {
319 (self.flags & Self::VERSION_MASK) >> Self::VERSION_SHIFT
320 }
321
322 #[inline]
324 pub fn is_gtp(&self) -> bool {
325 self.flags & Self::FLAG_PT != 0
326 }
327
328 #[inline]
330 pub fn is_gtp_prime(&self) -> bool {
331 !self.is_gtp()
332 }
333
334 #[inline]
336 pub fn has_extension(&self) -> bool {
337 self.flags & Self::FLAG_E != 0
338 }
339
340 #[inline]
342 pub fn has_sequence(&self) -> bool {
343 self.flags & Self::FLAG_S != 0
344 }
345
346 #[inline]
348 pub fn has_npdu(&self) -> bool {
349 self.flags & Self::FLAG_PN != 0
350 }
351
352 #[inline]
354 pub fn has_optional_fields(&self) -> bool {
355 self.flags & (Self::FLAG_E | Self::FLAG_S | Self::FLAG_PN) != 0
356 }
357
358 #[inline]
360 pub fn message_type(&self) -> u8 {
361 self.message_type
362 }
363
364 #[inline]
366 pub fn message_type_enum(&self) -> Gtpv1MessageType {
367 self.message_type.into()
368 }
369
370 #[inline]
372 pub fn is_gpdu(&self) -> bool {
373 self.message_type == 0xFF
374 }
375
376 #[inline]
378 pub fn is_echo_request(&self) -> bool {
379 self.message_type == 1
380 }
381
382 #[inline]
384 pub fn is_echo_response(&self) -> bool {
385 self.message_type == 2
386 }
387
388 #[inline]
390 pub fn is_control_plane(&self) -> bool {
391 !self.is_gpdu()
392 }
393
394 #[inline]
396 pub fn is_user_plane(&self) -> bool {
397 self.is_gpdu()
398 }
399
400 #[inline]
402 pub fn length(&self) -> u16 {
403 self.length.get()
404 }
405
406 #[inline]
408 pub fn teid(&self) -> u32 {
409 self.teid.get()
410 }
411
412 #[inline]
414 pub fn header_length(&self) -> usize {
415 if self.has_optional_fields() {
416 Self::OPTIONAL_HEADER_LEN
417 } else {
418 Self::MIN_HEADER_LEN
419 }
420 }
421
422 #[inline]
424 fn is_valid(&self) -> bool {
425 if self.version() != Self::VERSION_1 {
427 return false;
428 }
429
430 if self.flags & Self::FLAG_RESERVED != 0 {
432 return false;
433 }
434
435 true
436 }
437
438 pub fn flags_string(&self) -> String {
440 let mut flags = Vec::new();
441
442 if self.is_gtp() {
443 flags.push("PT");
444 }
445 if self.has_extension() {
446 flags.push("E");
447 }
448 if self.has_sequence() {
449 flags.push("S");
450 }
451 if self.has_npdu() {
452 flags.push("PN");
453 }
454
455 if flags.is_empty() {
456 "none".to_string()
457 } else {
458 flags.join(",")
459 }
460 }
461}
462
463#[derive(Debug, Clone)]
465pub struct Gtpv1HeaderOpt<'a> {
466 pub header: &'a Gtpv1Header,
467 pub raw_options: &'a [u8],
468}
469
470impl<'a> Gtpv1HeaderOpt<'a> {
471 pub fn sequence_number(&self) -> Option<u16> {
473 if !self.header.has_optional_fields() {
474 return None;
475 }
476
477 if self.raw_options.len() < 2 {
478 return None;
479 }
480
481 Some(u16::from_be_bytes([
482 self.raw_options[0],
483 self.raw_options[1],
484 ]))
485 }
486
487 pub fn npdu_number(&self) -> Option<u8> {
489 if !self.header.has_optional_fields() {
490 return None;
491 }
492
493 if self.raw_options.len() < 3 {
494 return None;
495 }
496
497 Some(self.raw_options[2])
498 }
499
500 pub fn next_extension_type(&self) -> Option<u8> {
502 if !self.header.has_optional_fields() {
503 return None;
504 }
505
506 if self.raw_options.len() < 4 {
507 return None;
508 }
509
510 Some(self.raw_options[3])
511 }
512
513 pub fn next_extension_type_enum(&self) -> Option<Gtpv1ExtensionType> {
515 self.next_extension_type().map(|t| t.into())
516 }
517
518 pub fn has_extension_headers(&self) -> bool {
520 if !self.header.has_extension() {
521 return false;
522 }
523
524 self.next_extension_type().map(|t| t != 0).unwrap_or(false)
525 }
526
527 pub fn extension_headers(&self) -> Gtpv1ExtensionIter<'a> {
529 let ext_data = if self.header.has_optional_fields() && self.raw_options.len() >= 4 {
530 if self.has_extension_headers() {
534 &[] as &[u8]
538 } else {
539 &[] as &[u8]
540 }
541 } else {
542 &[] as &[u8]
543 };
544
545 Gtpv1ExtensionIter {
546 data: ext_data,
547 next_type: self.next_extension_type().unwrap_or(0),
548 }
549 }
550}
551
552impl std::ops::Deref for Gtpv1HeaderOpt<'_> {
553 type Target = Gtpv1Header;
554
555 #[inline]
556 fn deref(&self) -> &Self::Target {
557 self.header
558 }
559}
560
561#[derive(Debug, Clone)]
563pub struct Gtpv1Extension<'a> {
564 pub extension_type: Gtpv1ExtensionType,
566 pub content: &'a [u8],
568 pub next_type: u8,
570}
571
572impl<'a> Gtpv1Extension<'a> {
573 pub fn total_length(&self) -> usize {
575 self.content.len() + 2 }
580}
581
582pub struct Gtpv1ExtensionIter<'a> {
584 data: &'a [u8],
585 next_type: u8,
586}
587
588impl<'a> Iterator for Gtpv1ExtensionIter<'a> {
589 type Item = Gtpv1Extension<'a>;
590
591 fn next(&mut self) -> Option<Self::Item> {
592 if self.next_type == 0 || self.data.is_empty() {
594 return None;
595 }
596
597 if self.data.is_empty() {
602 return None;
603 }
604
605 let length_units = self.data[0] as usize;
606 if length_units == 0 {
607 return None;
608 }
609
610 let total_len = length_units * 4;
611 if self.data.len() < total_len {
612 return None;
613 }
614
615 let extension_type: Gtpv1ExtensionType = self.next_type.into();
616 let content = &self.data[1..total_len - 1];
617 let next_type = self.data[total_len - 1];
618
619 let extension = Gtpv1Extension {
620 extension_type,
621 content,
622 next_type,
623 };
624
625 self.data = &self.data[total_len..];
626 self.next_type = next_type;
627
628 Some(extension)
629 }
630}
631
632impl PacketHeader for Gtpv1Header {
633 const NAME: &'static str = "Gtpv1Header";
634 type InnerType = u8;
637
638 #[inline]
639 fn inner_type(&self) -> Self::InnerType {
640 self.message_type
641 }
642
643 #[inline]
645 fn total_len(&self, _buf: &[u8]) -> usize {
646 self.header_length()
647 }
648
649 #[inline]
651 fn is_valid(&self) -> bool {
652 self.is_valid()
653 }
654}
655
656impl HeaderParser for Gtpv1Header {
657 type Output<'a> = Gtpv1HeaderOpt<'a>;
658
659 #[inline]
660 fn into_view<'a>(header: &'a Self, raw_options: &'a [u8]) -> Self::Output<'a> {
661 Gtpv1HeaderOpt {
662 header,
663 raw_options,
664 }
665 }
666}
667
668impl fmt::Display for Gtpv1Header {
669 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
670 write!(
671 f,
672 "GTPv1 {} msg={} len={} teid=0x{:08x} flags=[{}]",
673 if self.is_gtp() { "GTP" } else { "GTP'" },
674 self.message_type_enum(),
675 self.length(),
676 self.teid(),
677 self.flags_string()
678 )
679 }
680}
681
682impl fmt::Display for Gtpv1HeaderOpt<'_> {
683 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
684 write!(
685 f,
686 "GTPv1 {} msg={} len={} teid=0x{:08x}",
687 if self.is_gtp() { "GTP" } else { "GTP'" },
688 self.message_type_enum(),
689 self.length(),
690 self.teid()
691 )?;
692
693 if let Some(seq) = self.sequence_number() {
694 write!(f, " seq={}", seq)?;
695 }
696
697 if let Some(npdu) = self.npdu_number() {
698 write!(f, " npdu={}", npdu)?;
699 }
700
701 if let Some(next_ext) = self.next_extension_type() {
702 if next_ext != 0 {
703 write!(f, " next_ext=0x{:02x}", next_ext)?;
704 }
705 }
706
707 Ok(())
708 }
709}
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714
715 #[test]
716 fn test_gtpv1_header_size() {
717 assert_eq!(std::mem::size_of::<Gtpv1Header>(), 8);
718 assert_eq!(Gtpv1Header::FIXED_LEN, 8);
719 }
720
721 #[test]
722 fn test_gtpv1_basic_header() {
723 let packet = vec![
725 0x30, 0xFF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x00,
731 ];
732
733 let (header, payload) = Gtpv1Header::from_bytes(&packet).unwrap();
734 assert_eq!(header.version(), 1);
735 assert!(header.is_gtp());
736 assert!(!header.is_gtp_prime());
737 assert_eq!(header.message_type(), 0xFF);
738 assert!(header.is_gpdu());
739 assert!(header.is_user_plane());
740 assert!(!header.is_control_plane());
741 assert_eq!(header.length(), 4);
742 assert_eq!(header.teid(), 1);
743 assert!(!header.has_optional_fields());
744 assert_eq!(header.header_length(), 8);
745 assert_eq!(payload.len(), 4);
746 }
747
748 #[test]
749 fn test_gtpv1_with_sequence() {
750 let packet = vec![
752 0x32, 0xFF, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x42, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
761 ];
762
763 let (header, payload) = Gtpv1Header::from_bytes(&packet).unwrap();
764 assert!(header.has_sequence());
765 assert!(header.has_optional_fields());
766 assert_eq!(header.header_length(), 12);
767 assert_eq!(header.sequence_number(), Some(0x0042));
768 assert_eq!(header.npdu_number(), Some(0));
769 assert_eq!(header.next_extension_type(), Some(0));
770 assert_eq!(payload.len(), 4);
771 }
772
773 #[test]
774 fn test_gtpv1_with_extension() {
775 let packet = vec![
777 0x34, 0xFF, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x45, 0x00, 0x00, 0x00,
786 ];
787
788 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
789 assert!(header.has_extension());
790 assert!(header.has_optional_fields());
791 assert_eq!(header.next_extension_type(), Some(0x85));
792 assert!(header.has_extension_headers());
793 }
794
795 #[test]
796 fn test_gtpv1_with_npdu() {
797 let packet = vec![
799 0x31, 0xFF, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00,
808 ];
809
810 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
811 assert!(header.has_npdu());
812 assert!(header.has_optional_fields());
813 assert_eq!(header.npdu_number(), Some(0x42));
814 }
815
816 #[test]
817 fn test_gtpv1_echo_request() {
818 let packet = vec![
820 0x32, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, ];
828
829 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
830 assert!(header.is_echo_request());
831 assert!(!header.is_gpdu());
832 assert!(header.is_control_plane());
833 assert_eq!(header.message_type_enum(), Gtpv1MessageType::EchoRequest);
834 }
835
836 #[test]
837 fn test_gtpv1_echo_response() {
838 let packet = vec![
840 0x32, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0E, 0x01, ];
849
850 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
851 assert!(header.is_echo_response());
852 assert_eq!(header.message_type_enum(), Gtpv1MessageType::EchoResponse);
853 }
854
855 #[test]
856 fn test_gtpv1_control_plane_message() {
857 let packet = vec![
859 0x32, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, ];
866
867 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
868 assert!(header.is_control_plane());
869 assert!(!header.is_user_plane());
870 assert_eq!(
871 header.message_type_enum(),
872 Gtpv1MessageType::CreatePdpContextRequest
873 );
874 }
875
876 #[test]
877 fn test_gtpv1_gtp_prime() {
878 let packet = vec![
880 0x20, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
886 ];
887
888 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
889 assert!(!header.is_gtp());
890 assert!(header.is_gtp_prime());
891 }
892
893 #[test]
894 fn test_gtpv1_invalid_version() {
895 let packet = vec![
897 0x00, 0xFF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
899 ];
900
901 let result = Gtpv1Header::from_bytes(&packet);
902 assert!(result.is_err());
903 }
904
905 #[test]
906 fn test_gtpv1_invalid_version_2() {
907 let packet = vec![
909 0x48, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
911 ];
912
913 let result = Gtpv1Header::from_bytes(&packet);
914 assert!(result.is_err());
915 }
916
917 #[test]
918 fn test_gtpv1_parsing_too_small() {
919 let packet = vec![0x30, 0xFF, 0x00, 0x04]; let result = Gtpv1Header::from_bytes(&packet);
921 assert!(result.is_err());
922 }
923
924 #[test]
925 fn test_gtpv1_flags_string() {
926 let packet = vec![
927 0x37, 0xFF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
929 ];
930
931 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
932 let flags = header.flags_string();
933 assert!(flags.contains("PT"));
934 assert!(flags.contains("E"));
935 assert!(flags.contains("S"));
936 assert!(flags.contains("PN"));
937 }
938
939 #[test]
940 fn test_gtpv1_display() {
941 let packet = vec![
942 0x30, 0xFF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x00,
945 ];
946
947 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
948 let display = format!("{}", *header);
949 assert!(display.contains("GTPv1"));
950 assert!(display.contains("G-PDU"));
951 }
952
953 #[test]
954 fn test_gtpv1_message_types() {
955 assert_eq!(Gtpv1MessageType::from(1), Gtpv1MessageType::EchoRequest);
956 assert_eq!(Gtpv1MessageType::from(2), Gtpv1MessageType::EchoResponse);
957 assert_eq!(Gtpv1MessageType::from(255), Gtpv1MessageType::GPdu);
958 assert_eq!(Gtpv1MessageType::from(254), Gtpv1MessageType::EndMarker);
959 assert_eq!(Gtpv1MessageType::from(200), Gtpv1MessageType::Unknown);
960 }
961
962 #[test]
963 fn test_gtpv1_extension_types() {
964 assert_eq!(
965 Gtpv1ExtensionType::from(0x00),
966 Gtpv1ExtensionType::NoMoreExtensions
967 );
968 assert_eq!(
969 Gtpv1ExtensionType::from(0x85),
970 Gtpv1ExtensionType::PduSessionContainer
971 );
972 assert_eq!(
973 Gtpv1ExtensionType::from(0xC0),
974 Gtpv1ExtensionType::PdcpPduNumber
975 );
976 assert_eq!(Gtpv1ExtensionType::from(0xAA), Gtpv1ExtensionType::Unknown);
977 }
978
979 #[test]
980 fn test_gtpv1_ports() {
981 assert!(is_gtpv1_c_port(2123));
982 assert!(is_gtpv1_u_port(2152));
983 assert!(!is_gtpv1_c_port(2152));
984 assert!(!is_gtpv1_u_port(2123));
985 }
986
987 #[test]
988 fn test_gtpv1_large_teid() {
989 let packet = vec![
990 0x30, 0xFF, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
992 ];
993
994 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
995 assert_eq!(header.teid(), 0xFFFFFFFF);
996 }
997
998 #[test]
999 fn test_gtpv1_end_marker() {
1000 let packet = vec![
1001 0x30, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ];
1006
1007 let (header, payload) = Gtpv1Header::from_bytes(&packet).unwrap();
1008 assert_eq!(header.message_type(), 254);
1009 assert_eq!(header.message_type_enum(), Gtpv1MessageType::EndMarker);
1010 assert!(payload.is_empty());
1011 }
1012
1013 #[test]
1014 fn test_gtpv1_error_indication() {
1015 let packet = vec![
1016 0x32, 0x1A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
1019 0x00, 0x00, 0x00, 0x00,
1021 ];
1022
1023 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
1024 assert_eq!(
1025 header.message_type_enum(),
1026 Gtpv1MessageType::ErrorIndication
1027 );
1028 }
1029
1030 #[test]
1031 fn test_gtpv1_header_opt_display() {
1032 let packet = vec![
1033 0x32, 0xFF, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x42, 0x05, 0x85, 0x00, 0x00, 0x00, 0x00,
1038 ];
1039
1040 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
1041 let display = format!("{}", header);
1042 assert!(display.contains("seq=66"));
1043 assert!(display.contains("npdu=5"));
1044 assert!(display.contains("next_ext=0x85"));
1045 }
1046
1047 #[test]
1048 fn test_gtpv1_all_flags_set() {
1049 let packet = vec![
1050 0x37, 0xFF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x02, 0x85, ];
1055
1056 let (header, _) = Gtpv1Header::from_bytes(&packet).unwrap();
1057 assert!(header.is_gtp());
1058 assert!(header.has_extension());
1059 assert!(header.has_sequence());
1060 assert!(header.has_npdu());
1061 assert!(header.has_optional_fields());
1062 assert_eq!(header.header_length(), 12);
1063 }
1064}