1use crate::server::gtp::codec::{
13 self, GtpV1Header, GtpV1Ie, GtpV1Message, GtpV2Header, GtpV2Ie, GtpV2Message, GtpVersion,
14};
15use anyhow::{bail, ensure, Context, Result};
16use serde_json::{json, Value};
17use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
18
19pub const MAX_DATAGRAM: usize = 65_535;
22pub const MAX_IES: usize = 256;
25pub const MAX_EVENT_PAYLOAD: usize = 1024;
27pub const MAX_INNER_PAYLOAD: usize = codec::MAX_GPDU_PAYLOAD_LEN - 28;
31
32pub const IF_S1U_ENB_GTPU: u8 = 0;
34pub const IF_S1U_SGW_GTPU: u8 = 1;
35pub const IF_S5S8_SGW_GTPU: u8 = 4;
36pub const IF_S5S8_PGW_GTPU: u8 = 5;
37pub const IF_S5S8_SGW_GTPC: u8 = 6;
38pub const IF_S5S8_PGW_GTPC: u8 = 7;
39pub const IF_S11_MME_GTPC: u8 = 10;
40pub const IF_S11S4_SGW_GTPC: u8 = 11;
41
42pub const V2_IE_MEI: u8 = 75;
43pub const V2_IE_AMBR: u8 = 72;
44pub const V2_IE_BEARER_QOS: u8 = 80;
45pub const V2_IE_SERVING_NETWORK: u8 = 83;
46pub const V2_IE_PDN_TYPE: u8 = 99;
47pub const V2_IE_SELECTION_MODE: u8 = 128;
48pub const V2_IE_CHARGING_ID: u8 = 94;
49pub const V1_IE_CHARGING_GATEWAY: u8 = 251;
50pub const V1_IE_PEER_ADDRESS: u8 = 133;
52
53pub const V1_ERROR_INDICATION: u8 = codec::V1_ERROR_INDICATION;
54pub const V1_END_MARKER: u8 = codec::V1_END_MARKER;
55pub const V1_G_PDU: u8 = codec::V1_G_PDU;
56pub const V2_VERSION_NOT_SUPPORTED: u8 = 3;
58
59pub fn interface_name(t: u8) -> &'static str {
60 match t {
61 IF_S1U_ENB_GTPU => "S1-U eNodeB GTP-U",
62 IF_S1U_SGW_GTPU => "S1-U SGW GTP-U",
63 2 => "S12 RNC GTP-U",
64 3 => "S12 SGW GTP-U",
65 IF_S5S8_SGW_GTPU => "S5/S8 SGW GTP-U",
66 IF_S5S8_PGW_GTPU => "S5/S8 PGW GTP-U",
67 IF_S5S8_SGW_GTPC => "S5/S8 SGW GTP-C",
68 IF_S5S8_PGW_GTPC => "S5/S8 PGW GTP-C",
69 IF_S11_MME_GTPC => "S11 MME GTP-C",
70 IF_S11S4_SGW_GTPC => "S11/S4 SGW GTP-C",
71 _ => "other",
72 }
73}
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77pub enum Role {
78 Gn,
80 S5,
82 S11,
84}
85
86impl Role {
87 pub fn version(self) -> GtpVersion {
88 match self {
89 Role::Gn => GtpVersion::V1,
90 _ => GtpVersion::V2,
91 }
92 }
93 pub fn as_str(self) -> &'static str {
94 match self {
95 Role::Gn => "gn",
96 Role::S5 => "s5",
97 Role::S11 => "s11",
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub enum Request {
106 Echo,
107 Create,
108 Modify,
109 Delete,
110}
111
112impl Request {
113 pub fn message_type(self, version: GtpVersion) -> u8 {
114 match (version, self) {
115 (_, Request::Echo) => 1,
116 (GtpVersion::V1, Request::Create) => codec::V1_CREATE_PDP_CONTEXT_REQUEST,
117 (GtpVersion::V1, Request::Modify) => codec::V1_UPDATE_PDP_CONTEXT_REQUEST,
118 (GtpVersion::V1, Request::Delete) => codec::V1_DELETE_PDP_CONTEXT_REQUEST,
119 (GtpVersion::V2, Request::Create) => codec::V2_CREATE_SESSION_REQUEST,
120 (GtpVersion::V2, Request::Modify) => codec::V2_MODIFY_BEARER_REQUEST,
121 (GtpVersion::V2, Request::Delete) => codec::V2_DELETE_SESSION_REQUEST,
122 }
123 }
124 pub fn response_type(self, version: GtpVersion) -> u8 {
126 self.message_type(version) + 1
127 }
128 pub fn name(self, version: GtpVersion) -> &'static str {
129 match version {
130 GtpVersion::V1 => codec::v1_message_name(self.message_type(version)),
131 GtpVersion::V2 => codec::v2_message_name(self.message_type(version)),
132 }
133 }
134}
135
136#[derive(Clone, Debug, PartialEq, Eq)]
138pub struct BearerQos {
139 pub qci: u8,
140 pub arp_priority_level: u8,
141 pub pre_emption_capability: bool,
142 pub pre_emption_vulnerability: bool,
143 pub mbr_uplink_kbps: u64,
144 pub mbr_downlink_kbps: u64,
145 pub gbr_uplink_kbps: u64,
146 pub gbr_downlink_kbps: u64,
147}
148
149impl Default for BearerQos {
150 fn default() -> Self {
151 Self {
152 qci: 9,
153 arp_priority_level: 8,
154 pre_emption_capability: false,
155 pre_emption_vulnerability: true,
156 mbr_uplink_kbps: 0,
157 mbr_downlink_kbps: 0,
158 gbr_uplink_kbps: 0,
159 gbr_downlink_kbps: 0,
160 }
161 }
162}
163
164const MAX_BITRATE_KBPS: u64 = 0xFF_FFFF_FFFF;
166
167impl BearerQos {
168 pub fn from_json(v: Option<&Value>) -> Result<Self> {
169 let mut q = Self::default();
170 let Some(v) = v else { return Ok(q) };
171 ensure!(v.is_object(), "qos is an object");
172 if let Some(x) = v.get("qci") {
173 q.qci = small(x, "qos.qci", 1, 254)? as u8;
174 }
175 if let Some(x) = v.get("arp_priority_level") {
176 q.arp_priority_level = small(x, "qos.arp_priority_level", 1, 15)? as u8;
177 }
178 if let Some(x) = v.get("pre_emption_capability") {
179 q.pre_emption_capability = x
180 .as_bool()
181 .context("qos.pre_emption_capability is true or false")?;
182 }
183 if let Some(x) = v.get("pre_emption_vulnerability") {
184 q.pre_emption_vulnerability = x
185 .as_bool()
186 .context("qos.pre_emption_vulnerability is true or false")?;
187 }
188 for (key, slot) in [
189 ("mbr_uplink_kbps", &mut q.mbr_uplink_kbps),
190 ("mbr_downlink_kbps", &mut q.mbr_downlink_kbps),
191 ("gbr_uplink_kbps", &mut q.gbr_uplink_kbps),
192 ("gbr_downlink_kbps", &mut q.gbr_downlink_kbps),
193 ] {
194 if let Some(x) = v.get(key) {
195 *slot = small(x, &format!("qos.{key}"), 0, MAX_BITRATE_KBPS)?;
196 }
197 }
198 Ok(q)
199 }
200
201 pub fn encode_v2(&self) -> Vec<u8> {
204 let mut out = Vec::with_capacity(22);
205 let pci = u8::from(!self.pre_emption_capability);
206 let pvi = u8::from(!self.pre_emption_vulnerability);
207 out.push((pci << 6) | ((self.arp_priority_level & 0x0F) << 2) | pvi);
208 out.push(self.qci);
209 for rate in [
210 self.mbr_uplink_kbps,
211 self.mbr_downlink_kbps,
212 self.gbr_uplink_kbps,
213 self.gbr_downlink_kbps,
214 ] {
215 out.extend_from_slice(&rate.to_be_bytes()[3..8]);
216 }
217 out
218 }
219
220 pub fn encode_v1(&self) -> Vec<u8> {
225 vec![self.arp_priority_level.clamp(1, 3), 0x0B, 0x92, 0x1F]
226 }
227}
228
229fn small(v: &Value, name: &str, min: u64, max: u64) -> Result<u64> {
230 let n = v
231 .as_u64()
232 .with_context(|| format!("{name} is a whole number"))?;
233 ensure!(
234 (min..=max).contains(&n),
235 "{name} is {n}, outside {min}..={max}"
236 );
237 Ok(n)
238}
239
240#[derive(Clone, Debug, PartialEq, Eq)]
242pub struct CreateParams {
243 pub imsi: String,
244 pub msisdn: Option<String>,
245 pub mei: Option<String>,
246 pub apn: String,
247 pub pdn_type: u8,
248 pub ebi: u8,
249 pub rat_type: u8,
250 pub mcc: String,
251 pub mnc: String,
252 pub selection_mode: u8,
253 pub qos: BearerQos,
254 pub ambr_uplink_kbps: u32,
255 pub ambr_downlink_kbps: u32,
256 pub request_dns: bool,
257 pub pgw_address: Option<IpAddr>,
258}
259
260fn digits(v: &Value, name: &str, min: usize, max: usize) -> Result<String> {
262 let s = v
263 .as_str()
264 .with_context(|| format!("{name} is a string of digits"))?;
265 ensure!(
266 s.chars().all(|c| c.is_ascii_digit()),
267 "{name} must be decimal digits only"
268 );
269 ensure!(
270 (min..=max).contains(&s.len()),
271 "{name} has {} digits, outside {min}..={max}",
272 s.len()
273 );
274 Ok(s.to_string())
275}
276
277pub const MAX_APN_LEN: usize = 100;
279
280pub fn rat_value(version: GtpVersion, name: &str) -> Result<u8> {
281 let n = name
282 .trim()
283 .to_ascii_uppercase()
284 .replace(['-', ' ', '_'], "");
285 let pairs: &[(&str, u8)] = &[
286 ("UTRAN", 1),
287 ("GERAN", 2),
288 ("WLAN", 3),
289 ("GAN", 4),
290 ("HSPAEVOLUTION", 5),
291 ("EUTRAN", 6),
292 ("EUTRANNBIOT", 8),
293 ("LTEM", 9),
294 ("NR", 10),
295 ];
296 let v = pairs
297 .iter()
298 .find(|(k, _)| *k == n)
299 .map(|(_, v)| *v)
300 .with_context(|| {
301 format!("rat_type {name:?} is not one of UTRAN, GERAN, WLAN, GAN, HSPA Evolution, EUTRAN, EUTRAN-NB-IoT, LTE-M, NR")
302 })?;
303 ensure!(
304 version == GtpVersion::V2 || v <= 6,
305 "rat_type {name:?} has no GTPv1 code"
306 );
307 Ok(v)
308}
309
310impl CreateParams {
311 pub fn from_action(role: Role, a: &Value) -> Result<Self> {
312 let version = role.version();
313 let imsi = digits(a.get("imsi").context("imsi is required")?, "imsi", 5, 15)?;
314 let msisdn = a
315 .get("msisdn")
316 .map(|v| digits(v, "msisdn", 1, 15))
317 .transpose()?;
318 let mei = a.get("mei").map(|v| digits(v, "mei", 14, 16)).transpose()?;
319 let apn = a
320 .get("apn")
321 .and_then(Value::as_str)
322 .context("apn is required (a dotted name such as \"internet\")")?
323 .to_string();
324 ensure!(
325 !apn.is_empty() && apn.len() <= MAX_APN_LEN,
326 "apn is 1..={MAX_APN_LEN} characters"
327 );
328 codec::encode_apn(&apn)?;
329 let pdn_type = match a
330 .get("pdn_type")
331 .and_then(Value::as_str)
332 .unwrap_or("ipv4")
333 .to_ascii_lowercase()
334 .as_str()
335 {
336 "ipv4" => 1,
337 "ipv6" => 2,
338 "ipv4v6" => 3,
339 other => bail!("pdn_type {other:?} is ipv4, ipv6 or ipv4v6"),
340 };
341 let ebi = match a.get("ebi") {
342 Some(v) => small(v, "ebi", 5, 15)? as u8,
343 None => 5,
344 };
345 let rat_type = match a.get("rat_type").and_then(Value::as_str) {
346 Some(r) => rat_value(version, r)?,
347 None if version == GtpVersion::V1 => 1,
348 None => 6,
349 };
350 let mcc = match a.get("mcc") {
351 Some(v) => digits(v, "mcc", 3, 3)?,
352 None => "001".into(),
353 };
354 let mnc = match a.get("mnc") {
355 Some(v) => digits(v, "mnc", 2, 3)?,
356 None => "01".into(),
357 };
358 let selection_mode = match a.get("selection_mode") {
359 Some(v) => small(v, "selection_mode", 0, 3)? as u8,
360 None => 0,
361 };
362 let qos = BearerQos::from_json(a.get("qos"))?;
363 let ambr = |k: &str| -> Result<u32> {
364 match a.get(k) {
365 Some(v) => Ok(small(v, k, 0, u32::MAX as u64)? as u32),
366 None => Ok(DEFAULT_AMBR_KBPS),
367 }
368 };
369 let ambr_uplink_kbps = ambr("ambr_uplink_kbps")?;
370 let ambr_downlink_kbps = ambr("ambr_downlink_kbps")?;
371 let request_dns = match a.get("request_dns") {
372 Some(v) => v.as_bool().context("request_dns is true or false")?,
373 None => true,
374 };
375 let pgw_address = match a.get("pgw_address").and_then(Value::as_str) {
376 Some(s) => Some(
377 s.parse::<IpAddr>()
378 .with_context(|| format!("pgw_address {s:?} is not an IP address"))?,
379 ),
380 None => None,
381 };
382 if role == Role::S11 {
383 ensure!(
384 pgw_address.is_some(),
385 "on S11 the SGW needs the PGW to forward to: give pgw_address"
386 );
387 }
388 Ok(Self {
389 imsi,
390 msisdn,
391 mei,
392 apn,
393 pdn_type,
394 ebi,
395 rat_type,
396 mcc,
397 mnc,
398 selection_mode,
399 qos,
400 ambr_uplink_kbps,
401 ambr_downlink_kbps,
402 request_dns,
403 pgw_address,
404 })
405 }
406}
407
408pub const DEFAULT_AMBR_KBPS: u32 = 100_000;
409
410#[derive(Clone, Copy, Debug)]
412pub struct Local {
413 pub control_ip: IpAddr,
414 pub user_ip: IpAddr,
415 pub control_teid: u32,
416 pub data_teid: u32,
417}
418
419pub fn encode_plmn(mcc: &str, mnc: &str) -> Vec<u8> {
422 let d = |s: &str, i: usize| s.as_bytes().get(i).map(|b| b - b'0').unwrap_or(0x0F);
423 vec![
424 (d(mcc, 1) << 4) | d(mcc, 0),
425 (d(mnc, 2) << 4) | d(mcc, 2),
426 (d(mnc, 1) << 4) | d(mnc, 0),
427 ]
428}
429
430fn tbcd_fixed(digits: &str, len: usize) -> Vec<u8> {
433 let mut v = codec::encode_tbcd(digits);
434 v.resize(len, 0xFF);
435 v
436}
437
438fn pco_dns_request(pdn_type: u8) -> Vec<u8> {
441 let mut out = vec![0x80];
442 if pdn_type != 2 {
443 out.extend_from_slice(&[0x00, 0x0D, 0x00]);
444 }
445 if pdn_type != 1 {
446 out.extend_from_slice(&[0x00, 0x03, 0x00]);
447 }
448 out
449}
450
451fn ip_octets(ip: IpAddr) -> Vec<u8> {
452 match ip {
453 IpAddr::V4(a) => a.octets().to_vec(),
454 IpAddr::V6(a) => a.octets().to_vec(),
455 }
456}
457
458fn v1ie(ie_type: u8, value: Vec<u8>) -> GtpV1Ie {
459 GtpV1Ie { ie_type, value }
460}
461
462fn v1_encode(message_type: u8, teid: u32, sequence: u32, ies: &[GtpV1Ie]) -> Vec<u8> {
463 GtpV1Message {
464 header: GtpV1Header::with_sequence(message_type, teid, sequence as u16),
465 body: codec::encode_v1_ies(ies),
466 }
467 .encode()
468}
469
470fn v2_encode(header: GtpV2Header, ies: Vec<GtpV2Ie>) -> Vec<u8> {
471 GtpV2Message { header, ies }.encode()
472}
473
474pub fn echo_request(version: GtpVersion, sequence: u32, recovery: u8) -> Vec<u8> {
477 match version {
478 GtpVersion::V1 => v1_encode(codec::V1_ECHO_REQUEST, 0, sequence, &[]),
479 GtpVersion::V2 => v2_encode(
480 GtpV2Header::without_teid(codec::V2_ECHO_REQUEST, sequence),
481 vec![GtpV2Ie::new(codec::V2_IE_RECOVERY, 0, vec![recovery])],
482 ),
483 }
484}
485
486pub fn echo_response(version: GtpVersion, sequence: u32, recovery: u8) -> Vec<u8> {
488 match version {
489 GtpVersion::V1 => v1_encode(
490 codec::V1_ECHO_RESPONSE,
491 0,
492 sequence,
493 &[v1ie(codec::V1_IE_RECOVERY, vec![recovery])],
494 ),
495 GtpVersion::V2 => v2_encode(
496 GtpV2Header::without_teid(codec::V2_ECHO_RESPONSE, sequence),
497 vec![GtpV2Ie::new(codec::V2_IE_RECOVERY, 0, vec![recovery])],
498 ),
499 }
500}
501
502pub fn create_request(
505 role: Role,
506 p: &CreateParams,
507 local: &Local,
508 sequence: u32,
509 recovery: u8,
510) -> Result<Vec<u8>> {
511 let apn = codec::encode_apn(&p.apn)?;
512 Ok(match role {
513 Role::Gn => {
514 let mut ies = vec![
516 v1ie(codec::V1_IE_IMSI, tbcd_fixed(&p.imsi, 8)),
517 v1ie(codec::V1_IE_RECOVERY, vec![recovery]),
518 v1ie(codec::V1_IE_SELECTION_MODE, vec![0xFC | p.selection_mode]),
519 v1ie(
520 codec::V1_IE_TEID_DATA_I,
521 local.data_teid.to_be_bytes().to_vec(),
522 ),
523 v1ie(
524 codec::V1_IE_TEID_CONTROL_PLANE,
525 local.control_teid.to_be_bytes().to_vec(),
526 ),
527 v1ie(codec::V1_IE_NSAPI, vec![p.ebi & 0x0F]),
528 v1ie(
529 codec::V1_IE_END_USER_ADDRESS,
530 vec![
531 0xF1,
532 match p.pdn_type {
533 1 => 0x21,
534 2 => 0x57,
535 _ => 0x8D,
536 },
537 ],
538 ),
539 v1ie(codec::V1_IE_ACCESS_POINT_NAME, apn),
540 ];
541 if p.request_dns {
542 ies.push(v1ie(
543 codec::V1_IE_PROTOCOL_CONFIG_OPTIONS,
544 pco_dns_request(p.pdn_type),
545 ));
546 }
547 ies.push(v1ie(codec::V1_IE_GSN_ADDRESS, ip_octets(local.control_ip)));
548 ies.push(v1ie(codec::V1_IE_GSN_ADDRESS, ip_octets(local.user_ip)));
549 if let Some(m) = &p.msisdn {
550 let mut v = vec![0x91];
552 v.extend(codec::encode_tbcd(m));
553 ies.push(v1ie(codec::V1_IE_MSISDN, v));
554 }
555 ies.push(v1ie(codec::V1_IE_QOS_PROFILE, p.qos.encode_v1()));
556 ies.push(v1ie(codec::V1_IE_RAT_TYPE, vec![p.rat_type]));
557 if let Some(m) = &p.mei {
558 ies.push(v1ie(codec::V1_IE_IMEISV, tbcd_fixed(m, 8)));
559 }
560 v1_encode(codec::V1_CREATE_PDP_CONTEXT_REQUEST, 0, sequence, &ies)
561 }
562 Role::S5 | Role::S11 => {
563 let sender_if = if role == Role::S5 {
564 IF_S5S8_SGW_GTPC
565 } else {
566 IF_S11_MME_GTPC
567 };
568 let mut ies = vec![GtpV2Ie::new(
569 codec::V2_IE_IMSI,
570 0,
571 codec::encode_tbcd(&p.imsi),
572 )];
573 if let Some(m) = &p.msisdn {
574 ies.push(GtpV2Ie::new(codec::V2_IE_MSISDN, 0, codec::encode_tbcd(m)));
575 }
576 if let Some(m) = &p.mei {
577 ies.push(GtpV2Ie::new(V2_IE_MEI, 0, codec::encode_tbcd(m)));
578 }
579 ies.push(GtpV2Ie::new(
580 V2_IE_SERVING_NETWORK,
581 0,
582 encode_plmn(&p.mcc, &p.mnc),
583 ));
584 ies.push(GtpV2Ie::new(codec::V2_IE_RAT_TYPE, 0, vec![p.rat_type]));
585 ies.push(GtpV2Ie::new(
586 codec::V2_IE_FTEID,
587 0,
588 codec::encode_fteid(sender_if, local.control_teid, local.control_ip),
589 ));
590 if let (Role::S11, Some(pgw)) = (role, p.pgw_address) {
591 ies.push(GtpV2Ie::new(
592 codec::V2_IE_FTEID,
593 1,
594 codec::encode_fteid(IF_S5S8_PGW_GTPC, 0, pgw),
595 ));
596 }
597 ies.push(GtpV2Ie::new(codec::V2_IE_APN, 0, apn));
598 ies.push(GtpV2Ie::new(
601 V2_IE_SELECTION_MODE,
602 0,
603 vec![p.selection_mode],
604 ));
605 ies.push(GtpV2Ie::new(V2_IE_PDN_TYPE, 0, vec![p.pdn_type]));
606 ies.push(GtpV2Ie::new(
607 codec::V2_IE_PAA,
608 0,
609 match p.pdn_type {
610 1 => vec![1, 0, 0, 0, 0],
611 2 => {
612 let mut v = vec![2, 64];
613 v.extend([0u8; 16]);
614 v
615 }
616 _ => {
617 let mut v = vec![3, 64];
618 v.extend([0u8; 20]);
619 v
620 }
621 },
622 ));
623 ies.push(GtpV2Ie::new(codec::V2_IE_APN_RESTRICTION, 0, vec![0]));
624 let mut ambr = p.ambr_uplink_kbps.to_be_bytes().to_vec();
625 ambr.extend(p.ambr_downlink_kbps.to_be_bytes());
626 ies.push(GtpV2Ie::new(V2_IE_AMBR, 0, ambr));
627 if p.request_dns {
628 ies.push(GtpV2Ie::new(
629 codec::V2_IE_PCO,
630 0,
631 pco_dns_request(p.pdn_type),
632 ));
633 }
634 let mut bearer = vec![GtpV2Ie::new(codec::V2_IE_EBI, 0, vec![p.ebi & 0x0F])];
635 if role == Role::S5 {
636 bearer.push(GtpV2Ie::new(
637 codec::V2_IE_FTEID,
638 2,
639 codec::encode_fteid(IF_S5S8_SGW_GTPU, local.data_teid, local.user_ip),
640 ));
641 }
642 bearer.push(GtpV2Ie::new(V2_IE_BEARER_QOS, 0, p.qos.encode_v2()));
643 ies.push(GtpV2Ie::new(
644 codec::V2_IE_BEARER_CONTEXT,
645 0,
646 codec::encode_v2_grouped(&bearer),
647 ));
648 ies.push(GtpV2Ie::new(codec::V2_IE_RECOVERY, 0, vec![recovery]));
649 v2_encode(
650 GtpV2Header::new(codec::V2_CREATE_SESSION_REQUEST, 0, sequence),
651 ies,
652 )
653 }
654 })
655}
656
657pub fn modify_request(
661 role: Role,
662 peer_control_teid: u32,
663 ebi: u8,
664 qos: &BearerQos,
665 local: &Local,
666 sequence: u32,
667) -> Vec<u8> {
668 match role {
669 Role::Gn => {
670 let ies = vec![
671 v1ie(
672 codec::V1_IE_TEID_DATA_I,
673 local.data_teid.to_be_bytes().to_vec(),
674 ),
675 v1ie(
676 codec::V1_IE_TEID_CONTROL_PLANE,
677 local.control_teid.to_be_bytes().to_vec(),
678 ),
679 v1ie(codec::V1_IE_NSAPI, vec![ebi & 0x0F]),
680 v1ie(codec::V1_IE_GSN_ADDRESS, ip_octets(local.control_ip)),
681 v1ie(codec::V1_IE_GSN_ADDRESS, ip_octets(local.user_ip)),
682 v1ie(codec::V1_IE_QOS_PROFILE, qos.encode_v1()),
683 ];
684 v1_encode(
685 codec::V1_UPDATE_PDP_CONTEXT_REQUEST,
686 peer_control_teid,
687 sequence,
688 &ies,
689 )
690 }
691 Role::S5 | Role::S11 => {
692 let (instance, iface) = if role == Role::S11 {
693 (0, IF_S1U_ENB_GTPU)
694 } else {
695 (1, IF_S5S8_SGW_GTPU)
696 };
697 let bearer = vec![
698 GtpV2Ie::new(codec::V2_IE_EBI, 0, vec![ebi & 0x0F]),
699 GtpV2Ie::new(
700 codec::V2_IE_FTEID,
701 instance,
702 codec::encode_fteid(iface, local.data_teid, local.user_ip),
703 ),
704 ];
705 v2_encode(
706 GtpV2Header::new(codec::V2_MODIFY_BEARER_REQUEST, peer_control_teid, sequence),
707 vec![GtpV2Ie::new(
708 codec::V2_IE_BEARER_CONTEXT,
709 0,
710 codec::encode_v2_grouped(&bearer),
711 )],
712 )
713 }
714 }
715}
716
717pub fn delete_request(role: Role, peer_control_teid: u32, ebi: u8, sequence: u32) -> Vec<u8> {
720 match role {
721 Role::Gn => v1_encode(
722 codec::V1_DELETE_PDP_CONTEXT_REQUEST,
723 peer_control_teid,
724 sequence,
725 &[
726 v1ie(codec::V1_IE_TEARDOWN_IND, vec![0xFF]),
727 v1ie(codec::V1_IE_NSAPI, vec![ebi & 0x0F]),
728 ],
729 ),
730 Role::S5 | Role::S11 => v2_encode(
731 GtpV2Header::new(
732 codec::V2_DELETE_SESSION_REQUEST,
733 peer_control_teid,
734 sequence,
735 ),
736 vec![GtpV2Ie::new(codec::V2_IE_EBI, 0, vec![ebi & 0x0F])],
737 ),
738 }
739}
740
741pub fn cause_response(
743 version: GtpVersion,
744 message_type: u8,
745 teid: u32,
746 sequence: u32,
747 cause: u8,
748) -> Vec<u8> {
749 match version {
750 GtpVersion::V1 => v1_encode(
751 message_type,
752 teid,
753 sequence,
754 &[v1ie(codec::V1_IE_CAUSE, vec![cause])],
755 ),
756 GtpVersion::V2 => v2_encode(
757 GtpV2Header::new(message_type, teid, sequence),
758 vec![GtpV2Ie::new(codec::V2_IE_CAUSE, 0, vec![cause, 0])],
759 ),
760 }
761}
762
763pub fn cause_name(version: GtpVersion, value: u8) -> String {
765 codec::CAUSES
766 .iter()
767 .find(|c| match version {
768 GtpVersion::V1 => c.v1 == value,
769 GtpVersion::V2 => c.v2 == value,
770 })
771 .map(|c| c.name.to_string())
772 .unwrap_or_else(|| match (version, value) {
773 (GtpVersion::V2, 64) => "context_not_found".into(),
774 (GtpVersion::V2, 96) => "imsi_imei_not_known".into(),
775 _ => format!("cause_{value}"),
776 })
777}
778
779pub fn cause_value(version: GtpVersion, v: &Value) -> Result<u8> {
781 if let Some(n) = v.as_u64() {
782 ensure!(n <= 255, "cause {n} is one octet");
783 return Ok(n as u8);
784 }
785 let name = v.as_str().context("cause is a name or a number")?;
786 let c = codec::cause_by_name(name).with_context(|| {
787 format!(
788 "cause {name:?} is not one of {}",
789 codec::cause_names().join(", ")
790 )
791 })?;
792 Ok(match version {
793 GtpVersion::V1 => c.v1,
794 GtpVersion::V2 => c.v2,
795 })
796}
797
798#[derive(Debug)]
804pub struct Control {
805 pub version: GtpVersion,
806 pub message_type: u8,
807 pub sequence: u32,
808 pub teid: u32,
809 pub v1: Vec<GtpV1Ie>,
810 pub v2: Vec<GtpV2Ie>,
811}
812
813impl Control {
814 pub fn name(&self) -> &'static str {
815 message_name(self.version, self.message_type)
816 }
817 pub fn is_peer_request(&self) -> bool {
823 match self.version {
824 GtpVersion::V1 => matches!(self.message_type, 16 | 18 | 20 | 22 | 27),
825 GtpVersion::V2 => matches!(self.message_type, 32 | 34 | 36 | 95 | 97 | 99 | 101),
826 }
827 }
828}
829
830pub fn message_name(version: GtpVersion, t: u8) -> &'static str {
832 match (version, t) {
833 (GtpVersion::V1, 22) => "Initiate PDP Context Activation Request",
834 (GtpVersion::V1, 23) => "Initiate PDP Context Activation Response",
835 (GtpVersion::V1, 27) => "PDU Notification Request",
836 (GtpVersion::V1, 28) => "PDU Notification Response",
837 (GtpVersion::V1, _) => codec::v1_message_name(t),
838 (GtpVersion::V2, 3) => "Version Not Supported Indication",
839 (GtpVersion::V2, 95) => "Create Bearer Request",
840 (GtpVersion::V2, 96) => "Create Bearer Response",
841 (GtpVersion::V2, 97) => "Update Bearer Request",
842 (GtpVersion::V2, 98) => "Update Bearer Response",
843 (GtpVersion::V2, 99) => "Delete Bearer Request",
844 (GtpVersion::V2, 100) => "Delete Bearer Response",
845 (GtpVersion::V2, 101) => "Delete PDN Connection Set Request",
846 (GtpVersion::V2, 102) => "Delete PDN Connection Set Response",
847 (GtpVersion::V2, _) => codec::v2_message_name(t),
848 }
849}
850
851pub fn decode_control(data: &[u8]) -> Result<Control> {
853 ensure!(
854 data.len() <= MAX_DATAGRAM,
855 "datagram of {} octets is past {MAX_DATAGRAM}",
856 data.len()
857 );
858 match codec::peek_version(data) {
859 Some(1) => {
860 let m = GtpV1Message::decode(data)?;
861 ensure!(m.header.protocol_type, "GTP' (charging) is not spoken here");
862 let ies = codec::parse_v1_ies(&m.body)?;
863 ensure!(ies.len() <= MAX_IES, "{} IEs is past {MAX_IES}", ies.len());
864 Ok(Control {
865 version: GtpVersion::V1,
866 message_type: m.header.message_type,
867 sequence: m
868 .header
869 .sequence
870 .map(u32::from)
871 .context("a GTPv1-C message without a sequence number cannot be matched")?,
872 teid: m.header.teid,
873 v1: ies,
874 v2: vec![],
875 })
876 }
877 Some(2) => {
878 let m = GtpV2Message::decode(data)?;
879 ensure!(
880 m.ies.len() <= MAX_IES,
881 "{} IEs is past {MAX_IES}",
882 m.ies.len()
883 );
884 Ok(Control {
885 version: GtpVersion::V2,
886 message_type: m.header.message_type,
887 sequence: m.header.sequence,
888 teid: m.header.teid.unwrap_or(0),
889 v1: vec![],
890 v2: m.ies,
891 })
892 }
893 Some(v) => bail!("GTP version {v} is not spoken here"),
894 None => bail!("empty datagram"),
895 }
896}
897
898pub fn addr(value: &[u8]) -> Option<IpAddr> {
900 match value.len() {
901 4 => Some(IpAddr::V4(Ipv4Addr::new(
902 value[0], value[1], value[2], value[3],
903 ))),
904 16 => {
905 let mut o = [0u8; 16];
906 o.copy_from_slice(value);
907 Some(IpAddr::V6(Ipv6Addr::from(o)))
908 }
909 _ => None,
910 }
911}
912
913pub fn pco_dns(value: &[u8]) -> Vec<String> {
916 let mut out = Vec::new();
917 let mut pos = 1usize;
918 while pos + 3 <= value.len() {
919 let id = u16::from_be_bytes([value[pos], value[pos + 1]]);
920 let len = value[pos + 2] as usize;
921 let Some(c) = value.get(pos + 3..pos + 3 + len) else {
922 break;
923 };
924 match id {
925 0x000D | 0x0003 => {
926 if let Some(a) = addr(c) {
927 out.push(a.to_string());
928 }
929 }
930 0x8021 if c.len() >= 4 => {
931 let total = (u16::from_be_bytes([c[2], c[3]]) as usize).min(c.len());
932 let mut o = 4usize;
933 while o + 2 <= total {
934 let (t, l) = (c[o], c[o + 1] as usize);
935 if l < 2 || o + l > total {
936 break;
937 }
938 if (t == 129 || t == 131) && l == 6 {
939 out.push(Ipv4Addr::new(c[o + 2], c[o + 3], c[o + 4], c[o + 5]).to_string());
940 }
941 o += l;
942 }
943 }
944 _ => {}
945 }
946 pos += 3 + len;
947 }
948 out
949}
950
951fn fteid_json(instance: u8, value: &[u8]) -> Option<Value> {
952 let (iface, teid, address) = codec::decode_fteid(value)?;
953 Some(json!({
954 "instance": instance,
955 "interface": interface_name(iface),
956 "interface_type": iface,
957 "teid": teid,
958 "address": address.map(|a| a.to_string()),
959 }))
960}
961
962#[derive(Debug, Default)]
965pub struct Answer {
966 pub cause: Option<u8>,
967 pub peer_control_teid: Option<u32>,
968 pub peer_control_address: Option<IpAddr>,
969 pub peer_data_teid: Option<u32>,
970 pub peer_data_address: Option<IpAddr>,
971 pub ue_address: Option<IpAddr>,
972 pub data: Value,
973}
974
975pub fn read_answer(role: Role, c: &Control) -> Answer {
978 let mut a = Answer {
979 data: json!({}),
980 ..Default::default()
981 };
982 match c.version {
983 GtpVersion::V1 => {
984 let find = |t| codec::find_v1(&c.v1, t);
985 a.cause = find(codec::V1_IE_CAUSE).and_then(|v| v.first().copied());
986 if let Some(r) = find(codec::V1_IE_RECOVERY).and_then(|v| v.first()) {
987 a.data["recovery"] = json!(r);
988 }
989 a.peer_data_teid = find(codec::V1_IE_TEID_DATA_I)
990 .map(|v| u32::from_be_bytes([v[0], v[1], v[2], v[3]]));
991 a.peer_control_teid = find(codec::V1_IE_TEID_CONTROL_PLANE)
992 .map(|v| u32::from_be_bytes([v[0], v[1], v[2], v[3]]));
993 if let Some(v) = find(codec::V1_IE_CHARGING_ID) {
994 a.data["charging_id"] = json!(u32::from_be_bytes([v[0], v[1], v[2], v[3]]));
995 }
996 if let Some(v) = find(codec::V1_IE_END_USER_ADDRESS) {
997 let (t, ip) = codec::decode_end_user_address(v);
998 a.data["pdn_type"] = json!(t);
999 a.ue_address = ip;
1000 }
1001 if let Some(v) = find(codec::V1_IE_PROTOCOL_CONFIG_OPTIONS) {
1002 a.data["dns_servers"] = json!(pco_dns(v));
1003 }
1004 let gsn = codec::find_all_v1(&c.v1, codec::V1_IE_GSN_ADDRESS);
1005 a.peer_control_address = gsn.first().and_then(|v| addr(v));
1006 a.peer_data_address = gsn.get(1).and_then(|v| addr(v));
1007 }
1008 GtpVersion::V2 => {
1009 let top = |t, i| {
1010 c.v2.iter()
1011 .find(|ie| ie.ie_type == t && ie.instance == i)
1012 .map(|ie| ie.value.as_slice())
1013 };
1014 a.cause = top(codec::V2_IE_CAUSE, 0).and_then(|v| v.first().copied());
1015 if let Some(r) = top(codec::V2_IE_RECOVERY, 0).and_then(|v| v.first()) {
1016 a.data["recovery"] = json!(r);
1017 }
1018 if let Some(v) = top(codec::V2_IE_PAA, 0) {
1019 let (t, ip) = codec::decode_paa(v);
1020 a.data["pdn_type"] = json!(t);
1021 a.ue_address = ip;
1022 }
1023 if let Some(v) = top(codec::V2_IE_PCO, 0) {
1024 a.data["dns_servers"] = json!(pco_dns(v));
1025 }
1026 if let Some(v) = top(V2_IE_AMBR, 0).filter(|v| v.len() == 8) {
1027 a.data["ambr_uplink_kbps"] = json!(u32::from_be_bytes([v[0], v[1], v[2], v[3]]));
1028 a.data["ambr_downlink_kbps"] = json!(u32::from_be_bytes([v[4], v[5], v[6], v[7]]));
1029 }
1030 if let Some(v) = top(codec::V2_IE_APN_RESTRICTION, 0).and_then(|v| v.first()) {
1031 a.data["apn_restriction"] = json!(v);
1032 }
1033 let mut fteids = Vec::new();
1036 for ie in c.v2.iter().filter(|ie| ie.ie_type == codec::V2_IE_FTEID) {
1037 if let Some(j) = fteid_json(ie.instance, &ie.value) {
1038 fteids.push(j);
1039 }
1040 }
1041 let ctl = top(codec::V2_IE_FTEID, 0)
1042 .or_else(|| top(codec::V2_IE_FTEID, 1))
1043 .and_then(codec::decode_fteid);
1044 if let Some((_, teid, address)) = ctl {
1045 a.peer_control_teid = Some(teid);
1046 a.peer_control_address = address;
1047 }
1048 a.data["control_fteids"] = Value::Array(fteids);
1049 if let Some(inner) =
1051 top(codec::V2_IE_BEARER_CONTEXT, 0).and_then(|v| codec::parse_v2_grouped(v).ok())
1052 {
1053 let mut bearer = json!({});
1054 let mut data_fteids = Vec::new();
1055 for ie in &inner {
1056 match ie.ie_type {
1057 codec::V2_IE_EBI => {
1058 bearer["ebi"] = json!(ie.value.first().map(|v| v & 0x0F))
1059 }
1060 codec::V2_IE_CAUSE => {
1061 if let Some(v) = ie.value.first() {
1062 bearer["cause"] = json!(cause_name(GtpVersion::V2, *v));
1063 bearer["cause_value"] = json!(v);
1064 }
1065 }
1066 V2_IE_CHARGING_ID if ie.value.len() == 4 => {
1067 bearer["charging_id"] = json!(u32::from_be_bytes([
1068 ie.value[0],
1069 ie.value[1],
1070 ie.value[2],
1071 ie.value[3]
1072 ]))
1073 }
1074 codec::V2_IE_FTEID => {
1075 if let Some(j) = fteid_json(ie.instance, &ie.value) {
1076 data_fteids.push(j);
1077 }
1078 }
1079 _ => {}
1080 }
1081 }
1082 let wanted = if role == Role::S11 {
1083 IF_S1U_SGW_GTPU
1084 } else {
1085 IF_S5S8_PGW_GTPU
1086 };
1087 let user = inner
1088 .iter()
1089 .filter(|ie| ie.ie_type == codec::V2_IE_FTEID)
1090 .filter_map(|ie| codec::decode_fteid(&ie.value))
1091 .find(|f| f.0 == wanted)
1092 .or_else(|| {
1093 inner
1094 .iter()
1095 .filter(|ie| ie.ie_type == codec::V2_IE_FTEID)
1096 .find_map(|ie| codec::decode_fteid(&ie.value))
1097 });
1098 if let Some((_, teid, address)) = user {
1099 a.peer_data_teid = Some(teid);
1100 a.peer_data_address = address;
1101 }
1102 bearer["fteids"] = Value::Array(data_fteids);
1103 a.data["bearer"] = bearer;
1104 }
1105 }
1106 }
1107 if let Some(c2) = a.cause {
1108 a.data["cause"] = json!(cause_name(c.version, c2));
1109 a.data["cause_value"] = json!(c2);
1110 a.data["accepted"] = json!(codec::cause_accepts(c.version, c2));
1111 }
1112 if let Some(ip) = a.ue_address {
1113 a.data["ue_address"] = json!(ip.to_string());
1114 }
1115 if let Some(t) = a.peer_control_teid {
1116 a.data["peer_control_teid"] = json!(t);
1117 }
1118 if let Some(t) = a.peer_data_teid {
1119 a.data["peer_data_teid"] = json!(t);
1120 }
1121 if let Some(ip) = a.peer_data_address {
1122 a.data["peer_data_address"] = json!(ip.to_string());
1123 }
1124 a
1125}
1126
1127pub fn checksum(chunks: &[&[u8]]) -> u16 {
1133 let mut sum: u32 = 0;
1134 for chunk in chunks {
1135 let mut it = chunk.chunks_exact(2);
1136 for w in &mut it {
1137 sum += u16::from_be_bytes([w[0], w[1]]) as u32;
1138 }
1139 if let [b] = it.remainder() {
1140 sum += (*b as u32) << 8;
1141 }
1142 }
1143 while sum >> 16 != 0 {
1144 sum = (sum & 0xFFFF) + (sum >> 16);
1145 }
1146 !(sum as u16)
1147}
1148
1149#[derive(Clone, Debug, PartialEq, Eq)]
1151pub enum Inner {
1152 IcmpEcho {
1153 identifier: u16,
1154 sequence: u16,
1155 },
1156 Udp {
1157 source_port: u16,
1158 destination_port: u16,
1159 },
1160}
1161
1162pub fn ipv4_packet(
1165 source: Ipv4Addr,
1166 destination: Ipv4Addr,
1167 ttl: u8,
1168 identification: u16,
1169 inner: &Inner,
1170 payload: &[u8],
1171) -> Result<Vec<u8>> {
1172 ensure!(
1173 payload.len() <= MAX_INNER_PAYLOAD,
1174 "payload of {} octets is past {MAX_INNER_PAYLOAD}",
1175 payload.len()
1176 );
1177 let (protocol, transport) = match inner {
1178 Inner::IcmpEcho {
1179 identifier,
1180 sequence,
1181 } => {
1182 let mut t = vec![8, 0, 0, 0];
1183 t.extend(identifier.to_be_bytes());
1184 t.extend(sequence.to_be_bytes());
1185 t.extend_from_slice(payload);
1186 let c = checksum(&[&t]);
1187 t[2..4].copy_from_slice(&c.to_be_bytes());
1188 (1u8, t)
1189 }
1190 Inner::Udp {
1191 source_port,
1192 destination_port,
1193 } => {
1194 let len = (8 + payload.len()) as u16;
1195 let mut t = Vec::with_capacity(len as usize);
1196 t.extend(source_port.to_be_bytes());
1197 t.extend(destination_port.to_be_bytes());
1198 t.extend(len.to_be_bytes());
1199 t.extend([0, 0]);
1200 t.extend_from_slice(payload);
1201 let mut pseudo = Vec::with_capacity(12);
1202 pseudo.extend(source.octets());
1203 pseudo.extend(destination.octets());
1204 pseudo.extend([0, 17]);
1205 pseudo.extend(len.to_be_bytes());
1206 let mut c = checksum(&[&pseudo, &t]);
1207 if c == 0 {
1208 c = 0xFFFF;
1209 }
1210 t[6..8].copy_from_slice(&c.to_be_bytes());
1211 (17u8, t)
1212 }
1213 };
1214 let total = (20 + transport.len()) as u16;
1215 let mut ip = vec![0x45, 0];
1216 ip.extend(total.to_be_bytes());
1217 ip.extend(identification.to_be_bytes());
1218 ip.extend([0x40, 0]); ip.push(ttl);
1220 ip.push(protocol);
1221 ip.extend([0, 0]);
1222 ip.extend(source.octets());
1223 ip.extend(destination.octets());
1224 let c = checksum(&[&ip]);
1225 ip[10..12].copy_from_slice(&c.to_be_bytes());
1226 ip.extend(transport);
1227 Ok(ip)
1228}
1229
1230pub fn gpdu(teid: u32, sequence: u16, packet: &[u8]) -> Vec<u8> {
1232 GtpV1Message {
1233 header: GtpV1Header::with_sequence(codec::V1_G_PDU, teid, sequence),
1234 body: packet.to_vec(),
1235 }
1236 .encode()
1237}
1238
1239pub fn end_marker(teid: u32) -> Vec<u8> {
1241 GtpV1Message {
1242 header: GtpV1Header::new(codec::V1_END_MARKER, teid),
1243 body: vec![],
1244 }
1245 .encode()
1246}
1247
1248pub fn error_indication(teid: u32, sequence: u16, own_user_ip: IpAddr) -> Vec<u8> {
1251 GtpV1Message {
1252 header: GtpV1Header::with_sequence(codec::V1_ERROR_INDICATION, 0, sequence),
1253 body: codec::encode_v1_ies(&[
1254 v1ie(codec::V1_IE_TEID_DATA_I, teid.to_be_bytes().to_vec()),
1255 v1ie(V1_IE_PEER_ADDRESS, ip_octets(own_user_ip)),
1256 ]),
1257 }
1258 .encode()
1259}
1260
1261pub fn inner_json(packet: &[u8]) -> Value {
1264 let Some(ip) = codec::decode_inner_ip(packet) else {
1265 return json!({"decoded": false, "length": packet.len()});
1266 };
1267 let mut j = json!({
1268 "decoded": true,
1269 "version": ip.version,
1270 "source": ip.source.to_string(),
1271 "destination": ip.destination.to_string(),
1272 "protocol": ip.protocol_name,
1273 "protocol_number": ip.protocol,
1274 "ttl": ip.ttl,
1275 "length": ip.length,
1276 });
1277 let mut payload_at = ip.payload_offset;
1278 if ip.version == 4 {
1279 let ihl = (packet[0] & 0x0F) as usize * 4;
1280 if packet.len() >= ihl {
1281 j["ipv4_header_checksum_ok"] = json!(checksum(&[&packet[..ihl]]) == 0);
1282 let end = (ip.length as usize).clamp(ihl, packet.len());
1283 let transport = &packet[ihl..end];
1284 if ip.protocol == 1 && transport.len() >= 8 {
1285 j["icmp"] = json!({
1286 "type": transport[0],
1287 "type_name": match transport[0] { 0 => "echo_reply", 8 => "echo_request", 3 => "destination_unreachable", 11 => "time_exceeded", _ => "other" },
1288 "code": transport[1],
1289 "identifier": u16::from_be_bytes([transport[4], transport[5]]),
1290 "sequence": u16::from_be_bytes([transport[6], transport[7]]),
1291 "checksum_ok": checksum(&[transport]) == 0,
1292 });
1293 payload_at = Some(ihl + 8);
1294 }
1295 if ip.protocol == 17 && transport.len() >= 8 {
1296 j["source_port"] = json!(ip.source_port);
1297 j["destination_port"] = json!(ip.destination_port);
1298 let stored = u16::from_be_bytes([transport[6], transport[7]]);
1299 if stored != 0 {
1300 let mut pseudo = Vec::with_capacity(12);
1301 pseudo.extend(ip_octets(ip.source));
1302 pseudo.extend(ip_octets(ip.destination));
1303 pseudo.extend([0, 17]);
1304 pseudo.extend((transport.len() as u16).to_be_bytes());
1305 j["udp_checksum_ok"] = json!(checksum(&[&pseudo, transport]) == 0);
1306 }
1307 }
1308 }
1309 }
1310 if let Some(at) = payload_at.filter(|at| *at <= packet.len()) {
1311 let end = if ip.version == 4 {
1312 (ip.length as usize).clamp(at, packet.len())
1313 } else {
1314 packet.len()
1315 };
1316 let payload = &packet[at..end];
1317 let shown = &payload[..payload.len().min(MAX_EVENT_PAYLOAD)];
1318 let text = std::str::from_utf8(shown)
1319 .ok()
1320 .filter(|s| !crate::utils::sanitize::has_controls(&s.replace(char::is_whitespace, "")));
1321 match text {
1322 Some(t) => {
1323 j["payload"] = json!(t);
1324 j["payload_encoding"] = json!("utf8");
1325 }
1326 None => {
1327 j["payload"] = json!(hex::encode(shown));
1328 j["payload_encoding"] = json!("hex");
1329 }
1330 }
1331 j["payload_length"] = json!(payload.len());
1332 if payload.len() > shown.len() {
1333 j["payload_truncated"] = json!(true);
1334 }
1335 }
1336 j
1337}