1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
23
24pub const GTPC_PORT: u16 = 2123;
30pub const GTPU_PORT: u16 = 2152;
32
33pub const V1_MAX_SEQUENCE: u32 = 0xFFFF;
35
36pub const V2_MAX_SEQUENCE: u32 = 0x00FF_FFFF;
38
39pub const MAX_GPDU_PAYLOAD_LEN: usize = 65_495;
47
48pub const MAX_APN_LABEL_LEN: usize = 63;
53
54pub const V1_ECHO_REQUEST: u8 = 1;
59pub const V1_ECHO_RESPONSE: u8 = 2;
60pub const V1_VERSION_NOT_SUPPORTED: u8 = 3;
61pub const V1_CREATE_PDP_CONTEXT_REQUEST: u8 = 16;
62pub const V1_CREATE_PDP_CONTEXT_RESPONSE: u8 = 17;
63pub const V1_UPDATE_PDP_CONTEXT_REQUEST: u8 = 18;
64pub const V1_UPDATE_PDP_CONTEXT_RESPONSE: u8 = 19;
65pub const V1_DELETE_PDP_CONTEXT_REQUEST: u8 = 20;
66pub const V1_DELETE_PDP_CONTEXT_RESPONSE: u8 = 21;
67pub const V1_ERROR_INDICATION: u8 = 26;
68pub const V1_SUPPORTED_EXTENSION_HEADERS_NOTIFICATION: u8 = 31;
69pub const V1_END_MARKER: u8 = 254;
70pub const V1_G_PDU: u8 = 255;
71
72pub const V2_ECHO_REQUEST: u8 = 1;
73pub const V2_ECHO_RESPONSE: u8 = 2;
74pub const V2_CREATE_SESSION_REQUEST: u8 = 32;
75pub const V2_CREATE_SESSION_RESPONSE: u8 = 33;
76pub const V2_MODIFY_BEARER_REQUEST: u8 = 34;
77pub const V2_MODIFY_BEARER_RESPONSE: u8 = 35;
78pub const V2_DELETE_SESSION_REQUEST: u8 = 36;
79pub const V2_DELETE_SESSION_RESPONSE: u8 = 37;
80
81pub fn v1_message_name(t: u8) -> &'static str {
83 match t {
84 V1_ECHO_REQUEST => "Echo Request",
85 V1_ECHO_RESPONSE => "Echo Response",
86 V1_VERSION_NOT_SUPPORTED => "Version Not Supported",
87 V1_CREATE_PDP_CONTEXT_REQUEST => "Create PDP Context Request",
88 V1_CREATE_PDP_CONTEXT_RESPONSE => "Create PDP Context Response",
89 V1_UPDATE_PDP_CONTEXT_REQUEST => "Update PDP Context Request",
90 V1_UPDATE_PDP_CONTEXT_RESPONSE => "Update PDP Context Response",
91 V1_DELETE_PDP_CONTEXT_REQUEST => "Delete PDP Context Request",
92 V1_DELETE_PDP_CONTEXT_RESPONSE => "Delete PDP Context Response",
93 V1_ERROR_INDICATION => "Error Indication",
94 V1_SUPPORTED_EXTENSION_HEADERS_NOTIFICATION => "Supported Extension Headers Notification",
95 V1_END_MARKER => "End Marker",
96 V1_G_PDU => "G-PDU",
97 _ => "Unknown",
98 }
99}
100
101pub fn v2_message_name(t: u8) -> &'static str {
103 match t {
104 V2_ECHO_REQUEST => "Echo Request",
105 V2_ECHO_RESPONSE => "Echo Response",
106 V2_CREATE_SESSION_REQUEST => "Create Session Request",
107 V2_CREATE_SESSION_RESPONSE => "Create Session Response",
108 V2_MODIFY_BEARER_REQUEST => "Modify Bearer Request",
109 V2_MODIFY_BEARER_RESPONSE => "Modify Bearer Response",
110 V2_DELETE_SESSION_REQUEST => "Delete Session Request",
111 V2_DELETE_SESSION_RESPONSE => "Delete Session Response",
112 _ => "Unknown",
113 }
114}
115
116pub const V1_IE_CAUSE: u8 = 1;
121pub const V1_IE_IMSI: u8 = 2;
122pub const V1_IE_REORDERING_REQUIRED: u8 = 8;
123pub const V1_IE_RECOVERY: u8 = 14;
124pub const V1_IE_SELECTION_MODE: u8 = 15;
125pub const V1_IE_TEID_DATA_I: u8 = 16;
126pub const V1_IE_TEID_CONTROL_PLANE: u8 = 17;
127pub const V1_IE_TEARDOWN_IND: u8 = 19;
128pub const V1_IE_NSAPI: u8 = 20;
129pub const V1_IE_CHARGING_ID: u8 = 127;
130pub const V1_IE_END_USER_ADDRESS: u8 = 128;
131pub const V1_IE_ACCESS_POINT_NAME: u8 = 131;
132pub const V1_IE_PROTOCOL_CONFIG_OPTIONS: u8 = 132;
133pub const V1_IE_GSN_ADDRESS: u8 = 133;
134pub const V1_IE_MSISDN: u8 = 134;
135pub const V1_IE_QOS_PROFILE: u8 = 135;
136pub const V1_IE_RAT_TYPE: u8 = 151;
137pub const V1_IE_USER_LOCATION_INFO: u8 = 152;
138pub const V1_IE_MS_TIME_ZONE: u8 = 153;
139pub const V1_IE_IMEISV: u8 = 154;
140
141pub fn v1_fixed_ie_len(ie_type: u8) -> Option<usize> {
149 Some(match ie_type {
150 1 => 1, 2 => 8, 3 => 6, 4 => 4, 5 => 4, 8 => 1, 9 => 28, 11 => 1, 12 => 3, 13 => 1, 14 => 1, 15 => 1, 16 => 4, 17 => 4, 18 => 5, 19 => 1, 20 => 1, 21 => 1, 22 => 9, 23 => 1, 24 => 1, 25 => 2, 26 => 2, 27 => 2, 28 => 2, 29 => 1, 127 => 4, _ => return None,
178 })
179}
180
181pub fn v1_ie_name(ie_type: u8) -> &'static str {
183 match ie_type {
184 1 => "Cause",
185 2 => "IMSI",
186 3 => "Routeing Area Identity",
187 8 => "Reordering Required",
188 14 => "Recovery",
189 15 => "Selection Mode",
190 16 => "TEID Data I",
191 17 => "TEID Control Plane",
192 19 => "Teardown Ind",
193 20 => "NSAPI",
194 127 => "Charging ID",
195 128 => "End User Address",
196 129 => "MM Context",
197 130 => "PDP Context",
198 131 => "Access Point Name",
199 132 => "Protocol Configuration Options",
200 133 => "GSN Address",
201 134 => "MSISDN",
202 135 => "QoS Profile",
203 148 => "Common Flags",
204 149 => "APN Restriction",
205 151 => "RAT Type",
206 152 => "User Location Information",
207 153 => "MS Time Zone",
208 154 => "IMEI(SV)",
209 251 => "Charging Gateway Address",
210 255 => "Private Extension",
211 _ => "Unknown",
212 }
213}
214
215pub const V2_IE_IMSI: u8 = 1;
220pub const V2_IE_CAUSE: u8 = 2;
221pub const V2_IE_RECOVERY: u8 = 3;
222pub const V2_IE_APN: u8 = 71;
223pub const V2_IE_EBI: u8 = 73;
224pub const V2_IE_MSISDN: u8 = 76;
225pub const V2_IE_PCO: u8 = 78;
226pub const V2_IE_PAA: u8 = 79;
227pub const V2_IE_RAT_TYPE: u8 = 82;
228pub const V2_IE_FTEID: u8 = 87;
229pub const V2_IE_BEARER_CONTEXT: u8 = 93;
230pub const V2_IE_ULI: u8 = 86;
231pub const V2_IE_APN_RESTRICTION: u8 = 127;
232
233pub fn v2_ie_name(ie_type: u8) -> &'static str {
235 match ie_type {
236 1 => "IMSI",
237 2 => "Cause",
238 3 => "Recovery",
239 71 => "APN",
240 72 => "AMBR",
241 73 => "EPS Bearer ID",
242 74 => "IPv4 Configuration Parameters",
243 75 => "MEI",
244 76 => "MSISDN",
245 77 => "Indication",
246 78 => "Protocol Configuration Options",
247 79 => "PDN Address Allocation",
248 80 => "Bearer QoS",
249 82 => "RAT Type",
250 83 => "Serving Network",
251 86 => "User Location Information",
252 87 => "Fully Qualified TEID",
253 93 => "Bearer Context",
254 94 => "Charging ID",
255 99 => "PDN Type",
256 127 => "APN Restriction",
257 255 => "Private Extension",
258 _ => "Unknown",
259 }
260}
261
262#[derive(Clone, Debug, PartialEq, Eq)]
267pub enum DecodeError {
268 TooShort { len: usize, needed: usize },
270 BadVersion { version: u8 },
272 LengthMismatch { declared: usize, available: usize },
274 BadExtensionHeader { ext_type: u8 },
276 TruncatedIe { ie_type: u8 },
278 UnknownFixedIe { ie_type: u8 },
281}
282
283impl std::fmt::Display for DecodeError {
284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285 match self {
286 DecodeError::TooShort { len, needed } => {
287 write!(f, "datagram is {len} octets, needs at least {needed}")
288 }
289 DecodeError::BadVersion { version } => {
290 write!(
291 f,
292 "GTP version {version} is not supported (expected 1 or 2)"
293 )
294 }
295 DecodeError::LengthMismatch {
296 declared,
297 available,
298 } => write!(
299 f,
300 "header declares {declared} octets of payload but {available} are present"
301 ),
302 DecodeError::BadExtensionHeader { ext_type } => {
303 write!(f, "malformed extension header of type {ext_type}")
304 }
305 DecodeError::TruncatedIe { ie_type } => {
306 write!(f, "information element {ie_type} runs past the message")
307 }
308 DecodeError::UnknownFixedIe { ie_type } => write!(
309 f,
310 "information element {ie_type} is below 128 (fixed length) but its length is \
311 not known, so the rest of the message cannot be parsed"
312 ),
313 }
314 }
315}
316
317impl std::error::Error for DecodeError {}
318
319#[derive(Clone, Debug, PartialEq, Eq)]
329pub enum EncodeError {
330 ApnLabelTooLong { label: String, len: usize },
332}
333
334impl std::fmt::Display for EncodeError {
335 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336 match self {
337 EncodeError::ApnLabelTooLong { label, len } => write!(
338 f,
339 "APN label '{label}' is {len} octets, over the {MAX_APN_LABEL_LEN}-octet DNS \
340 label limit (TS 23.003 §9.1)"
341 ),
342 }
343 }
344}
345
346impl std::error::Error for EncodeError {}
347
348#[derive(Clone, Copy, Debug, PartialEq, Eq)]
350pub enum GtpVersion {
351 V1,
352 V2,
353}
354
355impl GtpVersion {
356 pub fn as_number(self) -> u8 {
357 match self {
358 GtpVersion::V1 => 1,
359 GtpVersion::V2 => 2,
360 }
361 }
362
363 pub fn sequence_bits(self) -> u8 {
365 match self {
366 GtpVersion::V1 => 16,
367 GtpVersion::V2 => 24,
368 }
369 }
370
371 pub fn max_sequence(self) -> u32 {
373 match self {
374 GtpVersion::V1 => V1_MAX_SEQUENCE,
375 GtpVersion::V2 => V2_MAX_SEQUENCE,
376 }
377 }
378}
379
380pub fn peek_version(data: &[u8]) -> Option<u8> {
385 data.first().map(|b| b >> 5)
386}
387
388#[derive(Clone, Debug, PartialEq, Eq)]
398pub struct ExtensionHeader {
399 pub ext_type: u8,
400 pub content: Vec<u8>,
401}
402
403#[derive(Clone, Debug, PartialEq, Eq)]
405pub struct GtpV1Header {
406 pub protocol_type: bool,
408 pub message_type: u8,
409 pub teid: u32,
410 pub sequence: Option<u16>,
412 pub npdu: Option<u8>,
414 pub extension_headers: Vec<ExtensionHeader>,
416}
417
418impl GtpV1Header {
419 pub fn new(message_type: u8, teid: u32) -> Self {
421 Self {
422 protocol_type: true,
423 message_type,
424 teid,
425 sequence: None,
426 npdu: None,
427 extension_headers: Vec::new(),
428 }
429 }
430
431 pub fn with_sequence(message_type: u8, teid: u32, sequence: u16) -> Self {
433 Self {
434 sequence: Some(sequence),
435 ..Self::new(message_type, teid)
436 }
437 }
438
439 pub fn optional_present(&self) -> bool {
442 self.sequence.is_some() || self.npdu.is_some() || !self.extension_headers.is_empty()
443 }
444
445 pub fn flags(&self) -> u8 {
447 let mut flags = 0b0010_0000; if self.protocol_type {
449 flags |= 0b0001_0000;
450 }
451 if !self.extension_headers.is_empty() {
452 flags |= 0b0000_0100;
453 }
454 if self.sequence.is_some() {
455 flags |= 0b0000_0010;
456 }
457 if self.npdu.is_some() {
458 flags |= 0b0000_0001;
459 }
460 flags
461 }
462}
463
464#[derive(Clone, Debug, PartialEq, Eq)]
469pub struct GtpV1Message {
470 pub header: GtpV1Header,
471 pub body: Vec<u8>,
472}
473
474impl GtpV1Message {
475 pub fn decode(data: &[u8]) -> Result<Self, DecodeError> {
476 if data.len() < 8 {
477 return Err(DecodeError::TooShort {
478 len: data.len(),
479 needed: 8,
480 });
481 }
482 let flags = data[0];
483 let version = flags >> 5;
484 if version != 1 {
485 return Err(DecodeError::BadVersion { version });
486 }
487 let protocol_type = flags & 0b0001_0000 != 0;
488 let e = flags & 0b0000_0100 != 0;
489 let s = flags & 0b0000_0010 != 0;
490 let pn = flags & 0b0000_0001 != 0;
491
492 let message_type = data[1];
493 let declared = u16::from_be_bytes([data[2], data[3]]) as usize;
494 let teid = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
495
496 let available = data.len() - 8;
499 if declared > available {
500 return Err(DecodeError::LengthMismatch {
501 declared,
502 available,
503 });
504 }
505 let payload = &data[8..8 + declared];
508
509 let mut pos = 0usize;
510 let (sequence, npdu, mut next_ext) = if e || s || pn {
511 if payload.len() < 4 {
513 return Err(DecodeError::TooShort {
514 len: data.len(),
515 needed: 12,
516 });
517 }
518 let seq = u16::from_be_bytes([payload[0], payload[1]]);
519 let np = payload[2];
520 let nx = payload[3];
521 pos = 4;
522 (
523 if s { Some(seq) } else { None },
524 if pn { Some(np) } else { None },
525 if e { nx } else { 0 },
526 )
527 } else {
528 (None, None, 0)
529 };
530
531 let mut extension_headers = Vec::new();
532 while next_ext != 0 {
533 if pos >= payload.len() {
534 return Err(DecodeError::BadExtensionHeader { ext_type: next_ext });
535 }
536 let units = payload[pos] as usize;
537 if units == 0 {
538 return Err(DecodeError::BadExtensionHeader { ext_type: next_ext });
539 }
540 let total = units * 4;
541 if pos + total > payload.len() {
542 return Err(DecodeError::BadExtensionHeader { ext_type: next_ext });
543 }
544 let content = payload[pos + 1..pos + total - 1].to_vec();
545 extension_headers.push(ExtensionHeader {
546 ext_type: next_ext,
547 content,
548 });
549 next_ext = payload[pos + total - 1];
550 pos += total;
551 }
552
553 Ok(GtpV1Message {
554 header: GtpV1Header {
555 protocol_type,
556 message_type,
557 teid,
558 sequence,
559 npdu,
560 extension_headers,
561 },
562 body: payload[pos..].to_vec(),
563 })
564 }
565
566 pub fn encode(&self) -> Vec<u8> {
567 let h = &self.header;
568 let mut tail = Vec::new();
569
570 if h.optional_present() {
571 tail.extend_from_slice(&h.sequence.unwrap_or(0).to_be_bytes());
574 tail.push(h.npdu.unwrap_or(0));
575 tail.push(h.extension_headers.first().map(|x| x.ext_type).unwrap_or(0));
576 }
577
578 for (i, ext) in h.extension_headers.iter().enumerate() {
579 let total = ext.content.len() + 2;
580 debug_assert_eq!(total % 4, 0, "extension header must be a multiple of 4");
581 tail.push((total / 4) as u8);
582 tail.extend_from_slice(&ext.content);
583 tail.push(
584 h.extension_headers
585 .get(i + 1)
586 .map(|x| x.ext_type)
587 .unwrap_or(0),
588 );
589 }
590
591 tail.extend_from_slice(&self.body);
592
593 let mut out = Vec::with_capacity(8 + tail.len());
594 out.push(h.flags());
595 out.push(h.message_type);
596 out.extend_from_slice(&(tail.len() as u16).to_be_bytes());
597 out.extend_from_slice(&h.teid.to_be_bytes());
598 out.extend_from_slice(&tail);
599 out
600 }
601}
602
603#[derive(Clone, Debug, PartialEq, Eq)]
608pub struct GtpV1Ie {
609 pub ie_type: u8,
610 pub value: Vec<u8>,
611}
612
613pub fn parse_v1_ies(body: &[u8]) -> Result<Vec<GtpV1Ie>, DecodeError> {
619 let mut out = Vec::new();
620 let mut pos = 0usize;
621 while pos < body.len() {
622 let ie_type = body[pos];
623 if ie_type < 128 {
624 let len = v1_fixed_ie_len(ie_type).ok_or(DecodeError::UnknownFixedIe { ie_type })?;
625 if pos + 1 + len > body.len() {
626 return Err(DecodeError::TruncatedIe { ie_type });
627 }
628 out.push(GtpV1Ie {
629 ie_type,
630 value: body[pos + 1..pos + 1 + len].to_vec(),
631 });
632 pos += 1 + len;
633 } else {
634 if pos + 3 > body.len() {
635 return Err(DecodeError::TruncatedIe { ie_type });
636 }
637 let len = u16::from_be_bytes([body[pos + 1], body[pos + 2]]) as usize;
638 if pos + 3 + len > body.len() {
639 return Err(DecodeError::TruncatedIe { ie_type });
640 }
641 out.push(GtpV1Ie {
642 ie_type,
643 value: body[pos + 3..pos + 3 + len].to_vec(),
644 });
645 pos += 3 + len;
646 }
647 }
648 Ok(out)
649}
650
651pub fn encode_v1_ies(ies: &[GtpV1Ie]) -> Vec<u8> {
653 let mut out = Vec::new();
654 for ie in ies {
655 out.push(ie.ie_type);
656 if ie.ie_type >= 128 {
657 out.extend_from_slice(&(ie.value.len() as u16).to_be_bytes());
658 }
659 out.extend_from_slice(&ie.value);
660 }
661 out
662}
663
664pub fn find_v1(ies: &[GtpV1Ie], ie_type: u8) -> Option<&[u8]> {
666 ies.iter()
667 .find(|ie| ie.ie_type == ie_type)
668 .map(|ie| ie.value.as_slice())
669}
670
671pub fn find_all_v1(ies: &[GtpV1Ie], ie_type: u8) -> Vec<&[u8]> {
673 ies.iter()
674 .filter(|ie| ie.ie_type == ie_type)
675 .map(|ie| ie.value.as_slice())
676 .collect()
677}
678
679#[derive(Clone, Debug, PartialEq, Eq)]
688pub struct GtpV2Header {
689 pub piggyback: bool,
690 pub message_priority: Option<u8>,
691 pub message_type: u8,
692 pub teid: Option<u32>,
694 pub sequence: u32,
696}
697
698impl GtpV2Header {
699 pub fn new(message_type: u8, teid: u32, sequence: u32) -> Self {
700 Self {
701 piggyback: false,
702 message_priority: None,
703 message_type,
704 teid: Some(teid),
705 sequence: sequence & 0x00FF_FFFF,
706 }
707 }
708
709 pub fn without_teid(message_type: u8, sequence: u32) -> Self {
711 Self {
712 teid: None,
713 ..Self::new(message_type, 0, sequence)
714 }
715 }
716
717 pub fn flags(&self) -> u8 {
718 let mut flags = 0b0100_0000; if self.piggyback {
720 flags |= 0b0001_0000;
721 }
722 if self.teid.is_some() {
723 flags |= 0b0000_1000;
724 }
725 if self.message_priority.is_some() {
726 flags |= 0b0000_0100;
727 }
728 flags
729 }
730}
731
732#[derive(Clone, Debug, PartialEq, Eq)]
733pub struct GtpV2Ie {
734 pub ie_type: u8,
735 pub instance: u8,
737 pub value: Vec<u8>,
738}
739
740impl GtpV2Ie {
741 pub fn new(ie_type: u8, instance: u8, value: Vec<u8>) -> Self {
742 Self {
743 ie_type,
744 instance,
745 value,
746 }
747 }
748}
749
750#[derive(Clone, Debug, PartialEq, Eq)]
751pub struct GtpV2Message {
752 pub header: GtpV2Header,
753 pub ies: Vec<GtpV2Ie>,
754}
755
756impl GtpV2Message {
757 pub fn decode(data: &[u8]) -> Result<Self, DecodeError> {
758 if data.len() < 4 {
759 return Err(DecodeError::TooShort {
760 len: data.len(),
761 needed: 4,
762 });
763 }
764 let flags = data[0];
765 let version = flags >> 5;
766 if version != 2 {
767 return Err(DecodeError::BadVersion { version });
768 }
769 let piggyback = flags & 0b0001_0000 != 0;
770 let t = flags & 0b0000_1000 != 0;
771 let mp = flags & 0b0000_0100 != 0;
772 let message_type = data[1];
773 let declared = u16::from_be_bytes([data[2], data[3]]) as usize;
774
775 let available = data.len() - 4;
776 if declared > available {
777 return Err(DecodeError::LengthMismatch {
778 declared,
779 available,
780 });
781 }
782 let rest = &data[4..4 + declared];
783
784 let fixed = if t { 8 } else { 4 };
786 if rest.len() < fixed {
787 return Err(DecodeError::TooShort {
788 len: data.len(),
789 needed: 4 + fixed,
790 });
791 }
792 let mut pos = 0usize;
793 let teid = if t {
794 let v = u32::from_be_bytes([rest[0], rest[1], rest[2], rest[3]]);
795 pos = 4;
796 Some(v)
797 } else {
798 None
799 };
800 let sequence =
801 u32::from_be_bytes([0, rest[pos], rest[pos + 1], rest[pos + 2]]) & 0x00FF_FFFF;
802 let last = rest[pos + 3];
803 pos += 4;
804
805 let mut ies = Vec::new();
806 while pos < rest.len() {
807 if pos + 4 > rest.len() {
808 return Err(DecodeError::TruncatedIe { ie_type: rest[pos] });
809 }
810 let ie_type = rest[pos];
811 let len = u16::from_be_bytes([rest[pos + 1], rest[pos + 2]]) as usize;
812 let instance = rest[pos + 3] & 0x0F;
813 if pos + 4 + len > rest.len() {
814 return Err(DecodeError::TruncatedIe { ie_type });
815 }
816 ies.push(GtpV2Ie {
817 ie_type,
818 instance,
819 value: rest[pos + 4..pos + 4 + len].to_vec(),
820 });
821 pos += 4 + len;
822 }
823
824 Ok(GtpV2Message {
825 header: GtpV2Header {
826 piggyback,
827 message_priority: if mp { Some(last >> 4) } else { None },
828 message_type,
829 teid,
830 sequence,
831 },
832 ies,
833 })
834 }
835
836 pub fn encode(&self) -> Vec<u8> {
837 let mut rest = Vec::new();
838 if let Some(teid) = self.header.teid {
839 rest.extend_from_slice(&teid.to_be_bytes());
840 }
841 let seq = self.header.sequence.to_be_bytes();
842 rest.extend_from_slice(&seq[1..4]);
843 rest.push(self.header.message_priority.map(|p| p << 4).unwrap_or(0));
844
845 for ie in &self.ies {
846 rest.push(ie.ie_type);
847 rest.extend_from_slice(&(ie.value.len() as u16).to_be_bytes());
848 rest.push(ie.instance & 0x0F);
849 rest.extend_from_slice(&ie.value);
850 }
851
852 let mut out = Vec::with_capacity(4 + rest.len());
853 out.push(self.header.flags());
854 out.push(self.header.message_type);
855 out.extend_from_slice(&(rest.len() as u16).to_be_bytes());
856 out.extend_from_slice(&rest);
857 out
858 }
859
860 pub fn find(&self, ie_type: u8) -> Option<&[u8]> {
861 self.ies
862 .iter()
863 .find(|ie| ie.ie_type == ie_type)
864 .map(|ie| ie.value.as_slice())
865 }
866
867 pub fn find_instance(&self, ie_type: u8, instance: u8) -> Option<&[u8]> {
868 self.ies
869 .iter()
870 .find(|ie| ie.ie_type == ie_type && ie.instance == instance)
871 .map(|ie| ie.value.as_slice())
872 }
873}
874
875pub fn parse_v2_grouped(value: &[u8]) -> Result<Vec<GtpV2Ie>, DecodeError> {
877 let mut out = Vec::new();
878 let mut pos = 0usize;
879 while pos < value.len() {
880 if pos + 4 > value.len() {
881 return Err(DecodeError::TruncatedIe {
882 ie_type: value[pos],
883 });
884 }
885 let ie_type = value[pos];
886 let len = u16::from_be_bytes([value[pos + 1], value[pos + 2]]) as usize;
887 let instance = value[pos + 3] & 0x0F;
888 if pos + 4 + len > value.len() {
889 return Err(DecodeError::TruncatedIe { ie_type });
890 }
891 out.push(GtpV2Ie {
892 ie_type,
893 instance,
894 value: value[pos + 4..pos + 4 + len].to_vec(),
895 });
896 pos += 4 + len;
897 }
898 Ok(out)
899}
900
901pub fn encode_v2_grouped(ies: &[GtpV2Ie]) -> Vec<u8> {
903 let mut out = Vec::new();
904 for ie in ies {
905 out.push(ie.ie_type);
906 out.extend_from_slice(&(ie.value.len() as u16).to_be_bytes());
907 out.push(ie.instance & 0x0F);
908 out.extend_from_slice(&ie.value);
909 }
910 out
911}
912
913pub fn encode_tbcd(digits: &str) -> Vec<u8> {
922 let nibbles: Vec<u8> = digits
923 .chars()
924 .filter(|c| c.is_ascii_digit())
925 .map(|c| c as u8 - b'0')
926 .collect();
927 let mut out = Vec::with_capacity(nibbles.len().div_ceil(2));
928 for pair in nibbles.chunks(2) {
929 let low = pair[0];
930 let high = pair.get(1).copied().unwrap_or(0x0F);
931 out.push((high << 4) | low);
932 }
933 out
934}
935
936pub fn decode_tbcd(bytes: &[u8]) -> String {
938 let mut out = String::with_capacity(bytes.len() * 2);
939 for b in bytes {
940 for nibble in [b & 0x0F, b >> 4] {
941 if nibble == 0x0F {
942 return out;
943 }
944 if nibble > 9 {
945 return out;
947 }
948 out.push((b'0' + nibble) as char);
949 }
950 }
951 out
952}
953
954pub fn encode_apn(apn: &str) -> Result<Vec<u8>, EncodeError> {
964 let mut out = Vec::with_capacity(apn.len() + 4);
965 for label in apn.split('.').filter(|l| !l.is_empty()) {
966 let bytes = label.as_bytes();
967 if bytes.len() > MAX_APN_LABEL_LEN {
968 return Err(EncodeError::ApnLabelTooLong {
969 label: crate::utils::truncate_for_log(label, 32),
970 len: bytes.len(),
971 });
972 }
973 out.push(bytes.len() as u8);
974 out.extend_from_slice(bytes);
975 }
976 Ok(out)
977}
978
979pub fn decode_apn(value: &[u8]) -> Option<String> {
982 let mut parts: Vec<String> = Vec::new();
983 let mut pos = 0usize;
984 while pos < value.len() {
985 let len = value[pos] as usize;
986 if len == 0 || pos + 1 + len > value.len() {
987 return None;
988 }
989 parts.push(String::from_utf8_lossy(&value[pos + 1..pos + 1 + len]).to_string());
990 pos += 1 + len;
991 }
992 if parts.is_empty() {
993 None
994 } else {
995 Some(parts.join("."))
996 }
997}
998
999pub fn encode_end_user_address(addr: Option<IpAddr>) -> Vec<u8> {
1004 match addr {
1005 Some(IpAddr::V4(v4)) => {
1006 let mut out = vec![0xF1, 0x21];
1007 out.extend_from_slice(&v4.octets());
1008 out
1009 }
1010 Some(IpAddr::V6(v6)) => {
1011 let mut out = vec![0xF1, 0x57];
1012 out.extend_from_slice(&v6.octets());
1013 out
1014 }
1015 None => vec![0xF1, 0x21],
1017 }
1018}
1019
1020pub fn decode_end_user_address(value: &[u8]) -> (&'static str, Option<IpAddr>) {
1023 if value.len() < 2 {
1024 return ("unknown", None);
1025 }
1026 let (name, expected) = match value[1] {
1027 0x21 => ("IPv4", 4usize),
1028 0x57 => ("IPv6", 16),
1029 0x8D => ("IPv4v6", 4),
1030 _ => ("unknown", 0),
1031 };
1032 let rest = &value[2..];
1033 if expected == 4 && rest.len() >= 4 {
1034 (
1035 name,
1036 Some(IpAddr::V4(Ipv4Addr::new(
1037 rest[0], rest[1], rest[2], rest[3],
1038 ))),
1039 )
1040 } else if expected == 16 && rest.len() >= 16 {
1041 let mut octets = [0u8; 16];
1042 octets.copy_from_slice(&rest[..16]);
1043 (name, Some(IpAddr::V6(Ipv6Addr::from(octets))))
1044 } else {
1045 (name, None)
1046 }
1047}
1048
1049pub fn encode_paa(addr: IpAddr) -> Vec<u8> {
1051 match addr {
1052 IpAddr::V4(v4) => {
1053 let mut out = vec![1u8];
1054 out.extend_from_slice(&v4.octets());
1055 out
1056 }
1057 IpAddr::V6(v6) => {
1058 let mut out = vec![2u8, 64];
1060 out.extend_from_slice(&v6.octets());
1061 out
1062 }
1063 }
1064}
1065
1066pub fn decode_paa(value: &[u8]) -> (&'static str, Option<IpAddr>) {
1068 match value.first() {
1069 Some(1) if value.len() >= 5 => (
1070 "IPv4",
1071 Some(IpAddr::V4(Ipv4Addr::new(
1072 value[1], value[2], value[3], value[4],
1073 ))),
1074 ),
1075 Some(2) if value.len() >= 18 => {
1076 let mut octets = [0u8; 16];
1077 octets.copy_from_slice(&value[2..18]);
1078 ("IPv6", Some(IpAddr::V6(Ipv6Addr::from(octets))))
1079 }
1080 Some(3) if value.len() >= 22 => {
1081 let mut octets = [0u8; 16];
1082 octets.copy_from_slice(&value[2..18]);
1083 (
1084 "IPv4v6",
1085 Some(IpAddr::V4(Ipv4Addr::new(
1086 value[18], value[19], value[20], value[21],
1087 ))),
1088 )
1089 }
1090 Some(1) => ("IPv4", None),
1091 Some(2) => ("IPv6", None),
1092 Some(3) => ("IPv4v6", None),
1093 _ => ("unknown", None),
1094 }
1095}
1096
1097pub fn encode_fteid(interface_type: u8, teid: u32, addr: IpAddr) -> Vec<u8> {
1102 let mut out = Vec::with_capacity(21);
1103 let flag = match addr {
1104 IpAddr::V4(_) => 0x80,
1105 IpAddr::V6(_) => 0x40,
1106 };
1107 out.push(flag | (interface_type & 0x3F));
1108 out.extend_from_slice(&teid.to_be_bytes());
1109 match addr {
1110 IpAddr::V4(v4) => out.extend_from_slice(&v4.octets()),
1111 IpAddr::V6(v6) => out.extend_from_slice(&v6.octets()),
1112 }
1113 out
1114}
1115
1116pub fn decode_fteid(value: &[u8]) -> Option<(u8, u32, Option<IpAddr>)> {
1118 if value.len() < 5 {
1119 return None;
1120 }
1121 let interface_type = value[0] & 0x3F;
1122 let teid = u32::from_be_bytes([value[1], value[2], value[3], value[4]]);
1123 let has_v4 = value[0] & 0x80 != 0;
1124 let has_v6 = value[0] & 0x40 != 0;
1125 let addr = if has_v4 && value.len() >= 9 {
1126 Some(IpAddr::V4(Ipv4Addr::new(
1127 value[5], value[6], value[7], value[8],
1128 )))
1129 } else if has_v6 && value.len() >= 21 {
1130 let mut octets = [0u8; 16];
1131 octets.copy_from_slice(&value[5..21]);
1132 Some(IpAddr::V6(Ipv6Addr::from(octets)))
1133 } else {
1134 None
1135 };
1136 Some((interface_type, teid, addr))
1137}
1138
1139pub fn encode_pco_dns(servers: &[IpAddr]) -> Vec<u8> {
1146 let mut out = vec![0x80u8];
1147 for server in servers {
1148 match server {
1149 IpAddr::V4(v4) => {
1150 out.extend_from_slice(&[0x00, 0x0D, 0x04]);
1151 out.extend_from_slice(&v4.octets());
1152 }
1153 IpAddr::V6(v6) => {
1154 out.extend_from_slice(&[0x00, 0x03, 0x10]);
1155 out.extend_from_slice(&v6.octets());
1156 }
1157 }
1158 }
1159 out
1160}
1161
1162pub fn rat_type_name(version: GtpVersion, value: u8) -> &'static str {
1165 match (version, value) {
1166 (GtpVersion::V1, 1) => "UTRAN",
1167 (GtpVersion::V1, 2) => "GERAN",
1168 (GtpVersion::V1, 3) => "WLAN",
1169 (GtpVersion::V1, 4) => "GAN",
1170 (GtpVersion::V1, 5) => "HSPA Evolution",
1171 (GtpVersion::V1, 6) => "EUTRAN",
1172 (GtpVersion::V2, 1) => "UTRAN",
1173 (GtpVersion::V2, 2) => "GERAN",
1174 (GtpVersion::V2, 3) => "WLAN",
1175 (GtpVersion::V2, 4) => "GAN",
1176 (GtpVersion::V2, 5) => "HSPA Evolution",
1177 (GtpVersion::V2, 6) => "EUTRAN",
1178 (GtpVersion::V2, 8) => "EUTRAN-NB-IoT",
1179 (GtpVersion::V2, 9) => "LTE-M",
1180 (GtpVersion::V2, 10) => "NR",
1181 _ => "unknown",
1182 }
1183}
1184
1185pub struct CauseName {
1195 pub name: &'static str,
1196 pub v1: u8,
1197 pub v2: u8,
1198 pub accepts: bool,
1201}
1202
1203pub const CAUSES: &[CauseName] = &[
1204 CauseName {
1205 name: "request_accepted",
1206 v1: 128,
1207 v2: 16,
1208 accepts: true,
1209 },
1210 CauseName {
1211 name: "new_pdp_type_due_to_network_preference",
1212 v1: 129,
1213 v2: 18,
1214 accepts: true,
1215 },
1216 CauseName {
1217 name: "new_pdp_type_due_to_single_address_bearer_only",
1218 v1: 130,
1219 v2: 19,
1220 accepts: true,
1221 },
1222 CauseName {
1223 name: "invalid_message_format",
1224 v1: 193,
1225 v2: 65,
1226 accepts: false,
1227 },
1228 CauseName {
1229 name: "imsi_not_known",
1230 v1: 194,
1231 v2: 95,
1232 accepts: false,
1233 },
1234 CauseName {
1235 name: "version_not_supported",
1236 v1: 198,
1237 v2: 66,
1238 accepts: false,
1239 },
1240 CauseName {
1241 name: "no_resources_available",
1242 v1: 199,
1243 v2: 73,
1244 accepts: false,
1245 },
1246 CauseName {
1247 name: "service_not_supported",
1248 v1: 200,
1249 v2: 68,
1250 accepts: false,
1251 },
1252 CauseName {
1253 name: "mandatory_ie_incorrect",
1254 v1: 201,
1255 v2: 69,
1256 accepts: false,
1257 },
1258 CauseName {
1259 name: "mandatory_ie_missing",
1260 v1: 202,
1261 v2: 70,
1262 accepts: false,
1263 },
1264 CauseName {
1265 name: "system_failure",
1266 v1: 204,
1267 v2: 72,
1268 accepts: false,
1269 },
1270 CauseName {
1271 name: "user_authentication_failed",
1272 v1: 209,
1273 v2: 91,
1274 accepts: false,
1275 },
1276 CauseName {
1277 name: "context_not_found",
1278 v1: 210,
1279 v2: 64,
1280 accepts: false,
1281 },
1282 CauseName {
1283 name: "all_dynamic_addresses_are_occupied",
1284 v1: 211,
1285 v2: 83,
1286 accepts: false,
1287 },
1288 CauseName {
1289 name: "no_memory_available",
1290 v1: 212,
1291 v2: 90,
1292 accepts: false,
1293 },
1294 CauseName {
1295 name: "missing_or_unknown_apn",
1296 v1: 219,
1297 v2: 77,
1298 accepts: false,
1299 },
1300 CauseName {
1301 name: "apn_access_denied_no_subscription",
1302 v1: 222,
1303 v2: 92,
1304 accepts: false,
1305 },
1306];
1307
1308pub fn cause_by_name(name: &str) -> Option<&'static CauseName> {
1311 let normalised: String = name
1312 .chars()
1313 .filter_map(|c| {
1314 if c.is_ascii_alphanumeric() {
1315 Some(c.to_ascii_lowercase())
1316 } else if c == ' ' || c == '-' || c == '_' {
1317 Some('_')
1318 } else {
1319 None
1320 }
1321 })
1322 .collect();
1323 CAUSES.iter().find(|c| c.name == normalised)
1324}
1325
1326pub fn cause_names() -> Vec<&'static str> {
1328 CAUSES.iter().map(|c| c.name).collect()
1329}
1330
1331pub fn cause_accepts(version: GtpVersion, value: u8) -> bool {
1337 match version {
1338 GtpVersion::V1 => (128..=191).contains(&value),
1339 GtpVersion::V2 => (16..=63).contains(&value),
1340 }
1341}
1342
1343#[derive(Clone, Debug, PartialEq, Eq)]
1352pub struct InnerIp {
1353 pub version: u8,
1354 pub source: IpAddr,
1355 pub destination: IpAddr,
1356 pub protocol: u8,
1357 pub protocol_name: &'static str,
1358 pub ttl: u8,
1360 pub length: u16,
1362 pub source_port: Option<u16>,
1363 pub destination_port: Option<u16>,
1364 pub payload_offset: Option<usize>,
1366}
1367
1368pub fn ip_protocol_name(protocol: u8) -> &'static str {
1369 match protocol {
1370 1 => "ICMP",
1371 2 => "IGMP",
1372 6 => "TCP",
1373 17 => "UDP",
1374 41 => "IPv6",
1375 47 => "GRE",
1376 50 => "ESP",
1377 51 => "AH",
1378 58 => "ICMPv6",
1379 89 => "OSPF",
1380 132 => "SCTP",
1381 _ => "unknown",
1382 }
1383}
1384
1385pub fn decode_inner_ip(packet: &[u8]) -> Option<InnerIp> {
1387 let first = *packet.first()?;
1388 match first >> 4 {
1389 4 => {
1390 if packet.len() < 20 {
1391 return None;
1392 }
1393 let ihl = (first & 0x0F) as usize * 4;
1394 if ihl < 20 {
1395 return None;
1396 }
1397 let protocol = packet[9];
1398 let source = IpAddr::V4(Ipv4Addr::new(
1399 packet[12], packet[13], packet[14], packet[15],
1400 ));
1401 let destination = IpAddr::V4(Ipv4Addr::new(
1402 packet[16], packet[17], packet[18], packet[19],
1403 ));
1404 let (source_port, destination_port, payload_offset) =
1405 transport_ports(packet, ihl, protocol);
1406 Some(InnerIp {
1407 version: 4,
1408 source,
1409 destination,
1410 protocol,
1411 protocol_name: ip_protocol_name(protocol),
1412 ttl: packet[8],
1413 length: u16::from_be_bytes([packet[2], packet[3]]),
1414 source_port,
1415 destination_port,
1416 payload_offset,
1417 })
1418 }
1419 6 => {
1420 if packet.len() < 40 {
1421 return None;
1422 }
1423 let protocol = packet[6];
1424 let mut s = [0u8; 16];
1425 s.copy_from_slice(&packet[8..24]);
1426 let mut d = [0u8; 16];
1427 d.copy_from_slice(&packet[24..40]);
1428 let (source_port, destination_port, payload_offset) =
1429 transport_ports(packet, 40, protocol);
1430 Some(InnerIp {
1431 version: 6,
1432 source: IpAddr::V6(Ipv6Addr::from(s)),
1433 destination: IpAddr::V6(Ipv6Addr::from(d)),
1434 protocol,
1435 protocol_name: ip_protocol_name(protocol),
1436 ttl: packet[7],
1437 length: u16::from_be_bytes([packet[4], packet[5]]),
1438 source_port,
1439 destination_port,
1440 payload_offset,
1441 })
1442 }
1443 _ => None,
1444 }
1445}
1446
1447fn transport_ports(
1448 packet: &[u8],
1449 offset: usize,
1450 protocol: u8,
1451) -> (Option<u16>, Option<u16>, Option<usize>) {
1452 if !matches!(protocol, 6 | 17 | 132) || packet.len() < offset + 4 {
1453 return (None, None, None);
1454 }
1455 let sport = u16::from_be_bytes([packet[offset], packet[offset + 1]]);
1456 let dport = u16::from_be_bytes([packet[offset + 2], packet[offset + 3]]);
1457 let payload_offset = match protocol {
1458 17 => Some(offset + 8),
1459 6 if packet.len() > offset + 12 => Some(offset + ((packet[offset + 12] >> 4) as usize * 4)),
1460 _ => None,
1461 };
1462 (Some(sport), Some(dport), payload_offset)
1463}