packet_strata/packet/tunnel/
gtpv1.rs

1//! GTPv1 (GPRS Tunneling Protocol version 1) parser
2//!
3//! This module implements parsing for GTPv1 as defined in 3GPP TS 29.060.
4//! GTPv1 is used for tunneling user data (GTP-U) and control signaling (GTP-C)
5//! in GPRS and LTE networks.
6//!
7//! # GTPv1 Header Format
8//!
9//! ```text
10//!  0                   1                   2                   3
11//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
12//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13//! |  Ver  |PT |(*)|E|S|PN|        Message Type                    |
14//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15//! |                         Length                                |
16//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17//! |                         TEID                                  |
18//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19//! |       Sequence Number (opt)   |   N-PDU (opt) | Next Ext (opt)|
20//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21//! ```
22//!
23//! # Ports
24//!
25//! - GTP-C (Control Plane): UDP port 2123
26//! - GTP-U (User Plane): UDP port 2152
27//!
28//! # Examples
29//!
30//! ## Basic GTPv1-U parsing
31//!
32//! ```
33//! use packet_strata::packet::tunnel::gtpv1::Gtpv1Header;
34//! use packet_strata::packet::HeaderParser;
35//!
36//! // GTPv1-U packet (G-PDU, message type 0xFF)
37//! let packet = vec![
38//!     0x30,        // Version 1, PT=1, no optional fields
39//!     0xFF,        // Message type: G-PDU
40//!     0x00, 0x04,  // Length: 4 bytes
41//!     0x00, 0x00, 0x00, 0x01,  // TEID: 1
42//!     // payload follows...
43//!     0x45, 0x00, 0x00, 0x00,  // Inner IP packet
44//! ];
45//!
46//! let (header, payload) = Gtpv1Header::from_bytes(&packet).unwrap();
47//! assert_eq!(header.version(), 1);
48//! assert!(header.is_gtp());
49//! assert_eq!(header.message_type(), 0xFF);
50//! assert_eq!(header.teid(), 1);
51//! ```
52//!
53//! ## GTPv1-U with sequence number
54//!
55//! ```
56//! use packet_strata::packet::tunnel::gtpv1::Gtpv1Header;
57//! use packet_strata::packet::HeaderParser;
58//!
59//! // GTPv1-U with sequence number
60//! let packet = vec![
61//!     0x32,        // Version 1, PT=1, S=1
62//!     0xFF,        // Message type: G-PDU
63//!     0x00, 0x08,  // Length: 8 bytes (4 optional + 4 payload)
64//!     0x00, 0x00, 0x00, 0x01,  // TEID: 1
65//!     0x00, 0x01,  // Sequence number: 1
66//!     0x00,        // N-PDU number: 0
67//!     0x00,        // Next extension: None
68//!     // payload follows...
69//!     0x45, 0x00, 0x00, 0x00,
70//! ];
71//!
72//! let (header, payload) = Gtpv1Header::from_bytes(&packet).unwrap();
73//! assert!(header.has_sequence());
74//! assert_eq!(header.sequence_number(), Some(1));
75//! ```
76
77use std::fmt::{self, Formatter};
78
79use zerocopy::byteorder::{BigEndian, U16, U32};
80use zerocopy::{FromBytes, IntoBytes, Unaligned};
81
82use crate::packet::{HeaderParser, PacketHeader};
83
84/// GTPv1-C standard port (Control Plane)
85pub const GTPV1_C_PORT: u16 = 2123;
86
87/// GTPv1-U standard port (User Plane)
88pub const GTPV1_U_PORT: u16 = 2152;
89
90/// Check if port is GTPv1-C
91#[inline]
92pub fn is_gtpv1_c_port(port: u16) -> bool {
93    port == GTPV1_C_PORT
94}
95
96/// Check if port is GTPv1-U
97#[inline]
98pub fn is_gtpv1_u_port(port: u16) -> bool {
99    port == GTPV1_U_PORT
100}
101
102/// GTPv1 Message Types
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104#[repr(u8)]
105pub enum Gtpv1MessageType {
106    // Path Management Messages
107    EchoRequest = 1,
108    EchoResponse = 2,
109    VersionNotSupported = 3,
110
111    // Tunnel Management Messages (GTP-C)
112    CreatePdpContextRequest = 16,
113    CreatePdpContextResponse = 17,
114    UpdatePdpContextRequest = 18,
115    UpdatePdpContextResponse = 19,
116    DeletePdpContextRequest = 20,
117    DeletePdpContextResponse = 21,
118
119    // Mobility Management Messages
120    SgsnContextRequest = 50,
121    SgsnContextResponse = 51,
122    SgsnContextAcknowledge = 52,
123
124    // Location Management Messages
125    SendRoutingInfoRequest = 32,
126    SendRoutingInfoResponse = 33,
127
128    // MBMS Messages
129    MbmsNotificationRequest = 96,
130    MbmsNotificationResponse = 97,
131    MbmsNotificationRejectRequest = 98,
132    MbmsNotificationRejectResponse = 99,
133
134    // GTP-U specific
135    ErrorIndication = 26,
136    SupportedExtensionHeadersNotification = 31,
137    EndMarker = 254,
138    GPdu = 255,
139
140    // Unknown
141    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/// GTPv1 Extension Header Types
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212#[repr(u8)]
213pub enum Gtpv1ExtensionType {
214    /// No more extension headers
215    NoMoreExtensions = 0x00,
216    /// MBMS support indication
217    MbmsSupportIndication = 0x01,
218    /// MS Info Change Reporting support indication
219    MsInfoChangeReporting = 0x02,
220    /// Long PDCP PDU Number
221    LongPdcpPduNumber = 0x03,
222    /// Service Class Indicator
223    ServiceClassIndicator = 0x20,
224    /// UDP Port
225    UdpPort = 0x40,
226    /// RAN Container
227    RanContainer = 0x81,
228    /// Long PDCP PDU Number (extended)
229    LongPdcpPduNumberExt = 0x82,
230    /// Xw RAN Container
231    XwRanContainer = 0x83,
232    /// NR RAN Container
233    NrRanContainer = 0x84,
234    /// PDU Session Container
235    PduSessionContainer = 0x85,
236    /// PDCP PDU Number
237    PdcpPduNumber = 0xC0,
238    /// Unknown extension type
239    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/// GTPv1 Header structure (fixed 8 bytes)
263///
264/// This is the mandatory part of the GTPv1 header.
265/// Optional fields (sequence number, N-PDU number, extension header type)
266/// are present when E, S, or PN flags are set.
267#[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    /// Version field mask (bits 5-7)
280    pub const VERSION_MASK: u8 = 0xE0;
281    pub const VERSION_SHIFT: u8 = 5;
282
283    /// Protocol Type (bit 4): 1 = GTP, 0 = GTP'
284    pub const FLAG_PT: u8 = 0x10;
285
286    /// Reserved bit (bit 3) - must be 0 in GTPv1
287    pub const FLAG_RESERVED: u8 = 0x08;
288
289    /// Extension Header flag (bit 2)
290    pub const FLAG_E: u8 = 0x04;
291
292    /// Sequence Number flag (bit 1)
293    pub const FLAG_S: u8 = 0x02;
294
295    /// N-PDU Number flag (bit 0)
296    pub const FLAG_PN: u8 = 0x01;
297
298    /// GTPv1 version number
299    pub const VERSION_1: u8 = 1;
300
301    /// Minimum header length (no optional fields)
302    pub const MIN_HEADER_LEN: usize = 8;
303
304    /// Header length with optional fields
305    pub const OPTIONAL_HEADER_LEN: usize = 12;
306
307    #[allow(unused)]
308    const NAME: &'static str = "Gtpv1Header";
309
310    /// Returns the flags byte
311    #[inline]
312    pub fn flags(&self) -> u8 {
313        self.flags
314    }
315
316    /// Returns the GTP version (should be 1)
317    #[inline]
318    pub fn version(&self) -> u8 {
319        (self.flags & Self::VERSION_MASK) >> Self::VERSION_SHIFT
320    }
321
322    /// Returns true if this is GTP (PT=1), false if GTP' (PT=0)
323    #[inline]
324    pub fn is_gtp(&self) -> bool {
325        self.flags & Self::FLAG_PT != 0
326    }
327
328    /// Returns true if this is GTP' (PT=0)
329    #[inline]
330    pub fn is_gtp_prime(&self) -> bool {
331        !self.is_gtp()
332    }
333
334    /// Returns true if Extension Header flag is set
335    #[inline]
336    pub fn has_extension(&self) -> bool {
337        self.flags & Self::FLAG_E != 0
338    }
339
340    /// Returns true if Sequence Number flag is set
341    #[inline]
342    pub fn has_sequence(&self) -> bool {
343        self.flags & Self::FLAG_S != 0
344    }
345
346    /// Returns true if N-PDU Number flag is set
347    #[inline]
348    pub fn has_npdu(&self) -> bool {
349        self.flags & Self::FLAG_PN != 0
350    }
351
352    /// Returns true if any optional field is present (E, S, or PN set)
353    #[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    /// Returns the message type
359    #[inline]
360    pub fn message_type(&self) -> u8 {
361        self.message_type
362    }
363
364    /// Returns the message type as enum
365    #[inline]
366    pub fn message_type_enum(&self) -> Gtpv1MessageType {
367        self.message_type.into()
368    }
369
370    /// Returns true if this is a G-PDU (user plane data)
371    #[inline]
372    pub fn is_gpdu(&self) -> bool {
373        self.message_type == 0xFF
374    }
375
376    /// Returns true if this is an Echo Request
377    #[inline]
378    pub fn is_echo_request(&self) -> bool {
379        self.message_type == 1
380    }
381
382    /// Returns true if this is an Echo Response
383    #[inline]
384    pub fn is_echo_response(&self) -> bool {
385        self.message_type == 2
386    }
387
388    /// Returns true if this is a control plane message
389    #[inline]
390    pub fn is_control_plane(&self) -> bool {
391        !self.is_gpdu()
392    }
393
394    /// Returns true if this is a user plane message (G-PDU)
395    #[inline]
396    pub fn is_user_plane(&self) -> bool {
397        self.is_gpdu()
398    }
399
400    /// Returns the length field (payload length, not including mandatory header)
401    #[inline]
402    pub fn length(&self) -> u16 {
403        self.length.get()
404    }
405
406    /// Returns the Tunnel Endpoint Identifier (TEID)
407    #[inline]
408    pub fn teid(&self) -> u32 {
409        self.teid.get()
410    }
411
412    /// Calculate the actual header length based on flags
413    #[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    /// Validates the GTPv1 header
423    #[inline]
424    fn is_valid(&self) -> bool {
425        // Version must be 1
426        if self.version() != Self::VERSION_1 {
427            return false;
428        }
429
430        // Reserved bit should be 0
431        if self.flags & Self::FLAG_RESERVED != 0 {
432            return false;
433        }
434
435        true
436    }
437
438    /// Returns a string representation of active flags
439    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/// GTPv1 Header with optional fields parsed
464#[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    /// Get the sequence number if present
472    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    /// Get the N-PDU number if present
488    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    /// Get the next extension header type if present
501    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    /// Get the next extension header type as enum
514    pub fn next_extension_type_enum(&self) -> Option<Gtpv1ExtensionType> {
515        self.next_extension_type().map(|t| t.into())
516    }
517
518    /// Returns true if there are extension headers to parse
519    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    /// Returns an iterator over extension headers
528    pub fn extension_headers(&self) -> Gtpv1ExtensionIter<'a> {
529        let ext_data = if self.header.has_optional_fields() && self.raw_options.len() >= 4 {
530            // Extension headers start after the 4-byte optional fields
531            // But the actual extension data comes from the payload portion
532            // The next_extension_type tells us if there are extensions
533            if self.has_extension_headers() {
534                // The raw_options only contains the 4 optional bytes
535                // Extensions would be in the payload, which we don't have direct access to here
536                // This is a limitation - extensions are actually part of the "length" field content
537                &[] 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/// Represents a single GTPv1 extension header
562#[derive(Debug, Clone)]
563pub struct Gtpv1Extension<'a> {
564    /// Extension header type
565    pub extension_type: Gtpv1ExtensionType,
566    /// Extension header content (excluding length and next type bytes)
567    pub content: &'a [u8],
568    /// Next extension header type
569    pub next_type: u8,
570}
571
572impl<'a> Gtpv1Extension<'a> {
573    /// Returns the total length of this extension header in bytes
574    pub fn total_length(&self) -> usize {
575        // Length field is in units of 4 bytes
576        // Format: [length][content...][next_type]
577        // where total = length * 4
578        self.content.len() + 2 // +1 for length byte, +1 for next_type byte
579    }
580}
581
582/// Iterator over GTPv1 extension headers
583pub 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        // No more extensions if next_type is 0
593        if self.next_type == 0 || self.data.is_empty() {
594            return None;
595        }
596
597        // Extension header format:
598        // [length (1 byte)][content (length*4 - 2 bytes)][next_type (1 byte)]
599        // where length is in units of 4 bytes
600
601        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    /// Inner type - for G-PDU this would be the encapsulated protocol
635    /// For simplicity, we return the message type as u8
636    type InnerType = u8;
637
638    #[inline]
639    fn inner_type(&self) -> Self::InnerType {
640        self.message_type
641    }
642
643    /// Returns the total header length in bytes (including optional fields)
644    #[inline]
645    fn total_len(&self, _buf: &[u8]) -> usize {
646        self.header_length()
647    }
648
649    /// Validates the GTPv1 header
650    #[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        // GTPv1-U G-PDU without optional fields
724        let packet = vec![
725            0x30, // Version 1, PT=1, E=0, S=0, PN=0
726            0xFF, // Message type: G-PDU
727            0x00, 0x04, // Length: 4 bytes
728            0x00, 0x00, 0x00, 0x01, // TEID: 1
729            // Payload
730            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        // GTPv1-U with sequence number
751        let packet = vec![
752            0x32, // Version 1, PT=1, S=1
753            0xFF, // Message type: G-PDU
754            0x00, 0x08, // Length: 8 bytes
755            0x00, 0x00, 0x00, 0x01, // TEID: 1
756            0x00, 0x42, // Sequence number: 66
757            0x00, // N-PDU number: 0
758            0x00, // Next extension: None
759            // Payload
760            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        // GTPv1-U with extension header flag
776        let packet = vec![
777            0x34, // Version 1, PT=1, E=1
778            0xFF, // Message type: G-PDU
779            0x00, 0x08, // Length: 8 bytes
780            0x00, 0x00, 0x00, 0x01, // TEID: 1
781            0x00, 0x00, // Sequence number: 0
782            0x00, // N-PDU number: 0
783            0x85, // Next extension: PDU Session Container
784            // Payload would include extension headers
785            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        // GTPv1-U with N-PDU number
798        let packet = vec![
799            0x31, // Version 1, PT=1, PN=1
800            0xFF, // Message type: G-PDU
801            0x00, 0x08, // Length: 8 bytes
802            0x00, 0x00, 0x00, 0x01, // TEID: 1
803            0x00, 0x00, // Sequence number: 0
804            0x42, // N-PDU number: 66
805            0x00, // Next extension: None
806            // Payload
807            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        // GTPv1 Echo Request
819        let packet = vec![
820            0x32, // Version 1, PT=1, S=1
821            0x01, // Message type: Echo Request
822            0x00, 0x04, // Length: 4 bytes
823            0x00, 0x00, 0x00, 0x00, // TEID: 0 (not used for echo)
824            0x00, 0x01, // Sequence number: 1
825            0x00, // N-PDU number
826            0x00, // Next extension
827        ];
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        // GTPv1 Echo Response
839        let packet = vec![
840            0x32, // Version 1, PT=1, S=1
841            0x02, // Message type: Echo Response
842            0x00, 0x06, // Length: 6 bytes (includes recovery IE)
843            0x00, 0x00, 0x00, 0x00, // TEID: 0
844            0x00, 0x01, // Sequence number: 1
845            0x00, // N-PDU number
846            0x00, // Next extension
847            0x0E, 0x01, // Recovery IE
848        ];
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        // Create PDP Context Request
858        let packet = vec![
859            0x32, // Version 1, PT=1, S=1
860            0x10, // Message type: Create PDP Context Request (16)
861            0x00, 0x04, // Length
862            0x00, 0x00, 0x00, 0x00, // TEID: 0
863            0x00, 0x01, // Sequence number
864            0x00, 0x00, // N-PDU, Next ext
865        ];
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        // GTP' (charging) - PT=0
879        let packet = vec![
880            0x20, // Version 1, PT=0
881            0x01, // Message type: Echo Request
882            0x00, 0x04, // Length
883            0x00, 0x00, 0x00, 0x00, // TEID
884            // Payload
885            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        // Invalid version (0)
896        let packet = vec![
897            0x00, // Version 0
898            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        // GTPv2 header (version 2) should fail
908        let packet = vec![
909            0x48, // Version 2, T=1
910            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]; // Only 4 bytes
920        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, // Version 1, PT=1, E=1, S=1, PN=1
928            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, // Version 1, PT=1
943            0xFF, // G-PDU
944            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, // TEID: max value
991            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, // Version 1, PT=1
1002            0xFE, // Message type: End Marker
1003            0x00, 0x00, // Length: 0
1004            0x00, 0x00, 0x00, 0x01, // TEID
1005        ];
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, // Version 1, PT=1, S=1
1017            0x1A, // Message type: Error Indication (26)
1018            0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
1019            // IEs would follow
1020            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, // Version 1, PT=1, S=1
1034            0xFF, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x42, // seq=66
1035            0x05, // npdu=5
1036            0x85, // next_ext=0x85
1037            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, // Version 1, PT=1, E=1, S=1, PN=1
1051            0xFF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, // seq
1052            0x02, // npdu
1053            0x85, // next_ext
1054        ];
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}