use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
use crate::types::Teid;
use std::net::{Ipv4Addr, Ipv6Addr};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OuterHeaderCreationFlags {
pub gtpu_udp_ipv4: bool,
pub gtpu_udp_ipv6: bool,
pub udp_ipv4: bool,
pub udp_ipv6: bool,
pub ipv4: bool,
pub ipv6: bool,
pub ctag: bool, pub stag: bool, }
impl OuterHeaderCreationFlags {
pub fn new() -> Self {
OuterHeaderCreationFlags {
gtpu_udp_ipv4: false,
gtpu_udp_ipv6: false,
udp_ipv4: false,
udp_ipv6: false,
ipv4: false,
ipv6: false,
ctag: false,
stag: false,
}
}
pub fn gtpu_ipv4() -> Self {
OuterHeaderCreationFlags {
gtpu_udp_ipv4: true,
..Self::new()
}
}
pub fn gtpu_ipv6() -> Self {
OuterHeaderCreationFlags {
gtpu_udp_ipv6: true,
..Self::new()
}
}
pub fn udp_ipv4() -> Self {
OuterHeaderCreationFlags {
udp_ipv4: true,
..Self::new()
}
}
pub fn udp_ipv6() -> Self {
OuterHeaderCreationFlags {
udp_ipv6: true,
..Self::new()
}
}
fn to_u16(self) -> u16 {
let mut flags = 0u16;
if self.gtpu_udp_ipv4 {
flags |= 0x0100; }
if self.gtpu_udp_ipv6 {
flags |= 0x0200; }
if self.udp_ipv4 {
flags |= 0x0400; }
if self.udp_ipv6 {
flags |= 0x0800; }
if self.ipv4 {
flags |= 0x1000; }
if self.ipv6 {
flags |= 0x2000; }
if self.ctag {
flags |= 0x4000; }
if self.stag {
flags |= 0x8000; }
flags
}
fn from_u16(value: u16) -> Self {
OuterHeaderCreationFlags {
gtpu_udp_ipv4: (value & 0x0100) != 0,
gtpu_udp_ipv6: (value & 0x0200) != 0,
udp_ipv4: (value & 0x0400) != 0,
udp_ipv6: (value & 0x0800) != 0,
ipv4: (value & 0x1000) != 0,
ipv6: (value & 0x2000) != 0,
ctag: (value & 0x4000) != 0,
stag: (value & 0x8000) != 0,
}
}
}
impl Default for OuterHeaderCreationFlags {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OuterHeaderCreation {
pub description: OuterHeaderCreationFlags,
pub teid: Option<Teid>, pub ipv4_address: Option<Ipv4Addr>,
pub ipv6_address: Option<Ipv6Addr>,
pub port_number: Option<u16>, pub ctag: Option<u32>, pub stag: Option<u32>, }
impl OuterHeaderCreation {
pub fn new(description: OuterHeaderCreationFlags) -> Self {
OuterHeaderCreation {
description,
teid: None,
ipv4_address: None,
ipv6_address: None,
port_number: None,
ctag: None,
stag: None,
}
}
pub fn gtpu_ipv4(teid: impl Into<Teid>, ipv4: Ipv4Addr) -> Self {
OuterHeaderCreation {
description: OuterHeaderCreationFlags::gtpu_ipv4(),
teid: Some(teid.into()),
ipv4_address: Some(ipv4),
ipv6_address: None,
port_number: None,
ctag: None,
stag: None,
}
}
pub fn gtpu_ipv6(teid: impl Into<Teid>, ipv6: Ipv6Addr) -> Self {
OuterHeaderCreation {
description: OuterHeaderCreationFlags::gtpu_ipv6(),
teid: Some(teid.into()),
ipv4_address: None,
ipv6_address: Some(ipv6),
port_number: None,
ctag: None,
stag: None,
}
}
pub fn udp_ipv4(ipv4: Ipv4Addr, port: u16) -> Self {
OuterHeaderCreation {
description: OuterHeaderCreationFlags::udp_ipv4(),
teid: None,
ipv4_address: Some(ipv4),
ipv6_address: None,
port_number: Some(port),
ctag: None,
stag: None,
}
}
pub fn with_ctag(mut self, ctag: u32) -> Self {
self.description.ctag = true;
self.ctag = Some(ctag & 0xFFFFFF); self
}
pub fn with_stag(mut self, stag: u32) -> Self {
self.description.stag = true;
self.stag = Some(stag & 0xFFFFFF); self
}
pub fn marshal(&self) -> Vec<u8> {
let mut data = Vec::new();
let desc_value = self.description.to_u16();
data.extend_from_slice(&desc_value.to_be_bytes());
if self.description.gtpu_udp_ipv4 || self.description.gtpu_udp_ipv6 {
let teid = self.teid.map(|t| t.0).unwrap_or(0);
data.extend_from_slice(&teid.to_be_bytes());
}
if self.description.gtpu_udp_ipv4 || self.description.udp_ipv4 || self.description.ipv4 {
if let Some(ipv4) = self.ipv4_address {
data.extend_from_slice(&ipv4.octets());
}
}
if self.description.gtpu_udp_ipv6 || self.description.udp_ipv6 || self.description.ipv6 {
if let Some(ipv6) = self.ipv6_address {
data.extend_from_slice(&ipv6.octets());
}
}
if self.description.udp_ipv4 || self.description.udp_ipv6 {
let port = self.port_number.unwrap_or(0);
data.extend_from_slice(&port.to_be_bytes());
}
if self.description.ctag {
if let Some(ctag) = self.ctag {
data.push(((ctag >> 16) & 0xFF) as u8);
data.push(((ctag >> 8) & 0xFF) as u8);
data.push((ctag & 0xFF) as u8);
}
}
if self.description.stag {
if let Some(stag) = self.stag {
data.push(((stag >> 16) & 0xFF) as u8);
data.push(((stag >> 8) & 0xFF) as u8);
data.push((stag & 0xFF) as u8);
}
}
data
}
pub fn unmarshal(payload: &[u8]) -> Result<Self, PfcpError> {
if payload.len() < 2 {
return Err(PfcpError::invalid_length(
"Outer Header Creation",
IeType::OuterHeaderCreation,
2,
payload.len(),
));
}
let mut offset = 0;
let desc_value = u16::from_be_bytes([payload[offset], payload[offset + 1]]);
let description = OuterHeaderCreationFlags::from_u16(desc_value);
offset += 2;
let teid = if description.gtpu_udp_ipv4 || description.gtpu_udp_ipv6 {
if offset + 4 > payload.len() {
return Err(PfcpError::invalid_length(
"Outer Header Creation TEID",
IeType::OuterHeaderCreation,
offset + 4,
payload.len(),
));
}
let teid_val = u32::from_be_bytes([
payload[offset],
payload[offset + 1],
payload[offset + 2],
payload[offset + 3],
]);
offset += 4;
Some(Teid(teid_val))
} else {
None
};
let ipv4_address = if description.gtpu_udp_ipv4 || description.udp_ipv4 || description.ipv4
{
if offset + 4 > payload.len() {
return Err(PfcpError::invalid_length(
"Outer Header Creation IPv4",
IeType::OuterHeaderCreation,
offset + 4,
payload.len(),
));
}
let ipv4 = Ipv4Addr::new(
payload[offset],
payload[offset + 1],
payload[offset + 2],
payload[offset + 3],
);
offset += 4;
Some(ipv4)
} else {
None
};
let ipv6_address = if description.gtpu_udp_ipv6 || description.udp_ipv6 || description.ipv6
{
if offset + 16 > payload.len() {
return Err(PfcpError::invalid_length(
"Outer Header Creation IPv6",
IeType::OuterHeaderCreation,
offset + 16,
payload.len(),
));
}
let mut ipv6_bytes = [0u8; 16];
ipv6_bytes.copy_from_slice(&payload[offset..offset + 16]);
offset += 16;
Some(Ipv6Addr::from(ipv6_bytes))
} else {
None
};
let port_number = if description.udp_ipv4 || description.udp_ipv6 {
if offset + 2 > payload.len() {
return Err(PfcpError::invalid_length(
"Outer Header Creation port",
IeType::OuterHeaderCreation,
offset + 2,
payload.len(),
));
}
let port = u16::from_be_bytes([payload[offset], payload[offset + 1]]);
offset += 2;
Some(port)
} else {
None
};
let ctag = if description.ctag {
if offset + 3 > payload.len() {
return Err(PfcpError::invalid_length(
"Outer Header Creation C-TAG",
IeType::OuterHeaderCreation,
offset + 3,
payload.len(),
));
}
let ctag_val = ((payload[offset] as u32) << 16)
| ((payload[offset + 1] as u32) << 8)
| (payload[offset + 2] as u32);
offset += 3;
Some(ctag_val)
} else {
None
};
let stag = if description.stag {
if offset + 3 > payload.len() {
return Err(PfcpError::invalid_length(
"Outer Header Creation S-TAG",
IeType::OuterHeaderCreation,
offset + 3,
payload.len(),
));
}
let stag_val = ((payload[offset] as u32) << 16)
| ((payload[offset + 1] as u32) << 8)
| (payload[offset + 2] as u32);
Some(stag_val)
} else {
None
};
Ok(OuterHeaderCreation {
description,
teid,
ipv4_address,
ipv6_address,
port_number,
ctag,
stag,
})
}
pub fn to_ie(&self) -> Ie {
Ie::new(IeType::OuterHeaderCreation, self.marshal())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_outer_header_creation_gtpu_ipv4() {
let ohc = OuterHeaderCreation::gtpu_ipv4(0x12345678, "192.168.1.1".parse().unwrap());
assert!(ohc.description.gtpu_udp_ipv4);
assert_eq!(ohc.teid, Some(Teid(0x12345678)));
assert_eq!(ohc.ipv4_address, Some("192.168.1.1".parse().unwrap()));
let marshaled = ohc.marshal();
let unmarshaled = OuterHeaderCreation::unmarshal(&marshaled).unwrap();
assert_eq!(ohc, unmarshaled);
}
#[test]
fn test_outer_header_creation_gtpu_ipv6() {
let ohc = OuterHeaderCreation::gtpu_ipv6(0xABCDEF01, "2001:db8::1".parse().unwrap());
assert!(ohc.description.gtpu_udp_ipv6);
assert_eq!(ohc.teid, Some(Teid(0xABCDEF01)));
assert_eq!(ohc.ipv6_address, Some("2001:db8::1".parse().unwrap()));
let marshaled = ohc.marshal();
let unmarshaled = OuterHeaderCreation::unmarshal(&marshaled).unwrap();
assert_eq!(ohc, unmarshaled);
}
#[test]
fn test_outer_header_creation_udp_ipv4() {
let ohc = OuterHeaderCreation::udp_ipv4("10.0.0.1".parse().unwrap(), 2152);
assert!(ohc.description.udp_ipv4);
assert_eq!(ohc.ipv4_address, Some("10.0.0.1".parse().unwrap()));
assert_eq!(ohc.port_number, Some(2152));
assert_eq!(ohc.teid, None);
let marshaled = ohc.marshal();
let unmarshaled = OuterHeaderCreation::unmarshal(&marshaled).unwrap();
assert_eq!(ohc, unmarshaled);
}
#[test]
fn test_outer_header_creation_with_ctag() {
let ohc = OuterHeaderCreation::gtpu_ipv4(0x1000, "192.168.1.1".parse().unwrap())
.with_ctag(0x123456);
assert!(ohc.description.ctag);
assert_eq!(ohc.ctag, Some(0x123456));
let marshaled = ohc.marshal();
let unmarshaled = OuterHeaderCreation::unmarshal(&marshaled).unwrap();
assert_eq!(ohc, unmarshaled);
}
#[test]
fn test_outer_header_creation_to_ie() {
let ohc = OuterHeaderCreation::gtpu_ipv4(0x12345678, "192.168.1.1".parse().unwrap());
let ie = ohc.to_ie();
assert_eq!(ie.ie_type, IeType::OuterHeaderCreation);
let unmarshaled = OuterHeaderCreation::unmarshal(&ie.payload).unwrap();
assert_eq!(ohc, unmarshaled);
}
#[test]
fn test_flags_round_trip() {
let flags = OuterHeaderCreationFlags {
gtpu_udp_ipv4: true,
udp_ipv6: true,
ctag: true,
..OuterHeaderCreationFlags::new()
};
let value = flags.to_u16();
let recovered = OuterHeaderCreationFlags::from_u16(value);
assert_eq!(flags, recovered);
}
}