use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
use std::net::{Ipv4Addr, Ipv6Addr};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AlternativeSmfIpAddress {
pub ipv4_address: Option<Ipv4Addr>,
pub ipv6_address: Option<Ipv6Addr>,
pub preferred_pfcp_entity: bool,
}
impl AlternativeSmfIpAddress {
pub fn new_ipv4(addr: Ipv4Addr) -> Self {
AlternativeSmfIpAddress {
ipv4_address: Some(addr),
ipv6_address: None,
preferred_pfcp_entity: false,
}
}
pub fn new_ipv6(addr: Ipv6Addr) -> Self {
AlternativeSmfIpAddress {
ipv4_address: None,
ipv6_address: Some(addr),
preferred_pfcp_entity: false,
}
}
pub fn new_dual_stack(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
AlternativeSmfIpAddress {
ipv4_address: Some(ipv4),
ipv6_address: Some(ipv6),
preferred_pfcp_entity: false,
}
}
pub fn with_preferred_pfcp_entity(mut self, preferred: bool) -> Self {
self.preferred_pfcp_entity = preferred;
self
}
pub fn marshal(&self) -> Vec<u8> {
let mut data = Vec::new();
let mut flags = 0u8;
if self.ipv6_address.is_some() {
flags |= 0x01; }
if self.ipv4_address.is_some() {
flags |= 0x02; }
if self.preferred_pfcp_entity {
flags |= 0x04; }
data.push(flags);
if let Some(ipv4) = &self.ipv4_address {
data.extend_from_slice(&ipv4.octets());
}
if let Some(ipv6) = &self.ipv6_address {
data.extend_from_slice(&ipv6.octets());
}
data
}
pub fn unmarshal(data: &[u8]) -> Result<Self, PfcpError> {
if data.is_empty() {
return Err(PfcpError::invalid_length(
"Alternative SMF IP Address",
IeType::AlternativeSmfIpAddress,
1,
0,
));
}
let flags = data[0];
let v6 = (flags & 0x01) != 0; let v4 = (flags & 0x02) != 0; let ppe = (flags & 0x04) != 0;
if (flags & 0xF8) != 0 {
return Err(PfcpError::invalid_value(
"Alternative SMF IP Address flags",
format!("0x{:02X}", flags),
"spare bits must be zero",
));
}
if !v4 && !v6 {
return Err(PfcpError::invalid_value(
"Alternative SMF IP Address flags",
format!("0x{:02X}", flags),
"at least one IP address must be present",
));
}
let mut offset = 1;
let mut ipv4_address = None;
let mut ipv6_address = None;
if v4 {
if data.len() < offset + 4 {
return Err(PfcpError::invalid_length(
"Alternative SMF IP Address (IPv4)",
IeType::AlternativeSmfIpAddress,
offset + 4,
data.len(),
));
}
let mut octets = [0u8; 4];
octets.copy_from_slice(&data[offset..offset + 4]);
ipv4_address = Some(Ipv4Addr::from(octets));
offset += 4;
}
if v6 {
if data.len() < offset + 16 {
return Err(PfcpError::invalid_length(
"Alternative SMF IP Address (IPv6)",
IeType::AlternativeSmfIpAddress,
offset + 16,
data.len(),
));
}
let mut octets = [0u8; 16];
octets.copy_from_slice(&data[offset..offset + 16]);
ipv6_address = Some(Ipv6Addr::from(octets));
}
Ok(AlternativeSmfIpAddress {
ipv4_address,
ipv6_address,
preferred_pfcp_entity: ppe,
})
}
pub fn to_ie(&self) -> Ie {
Ie::new(IeType::AlternativeSmfIpAddress, self.marshal())
}
}
impl From<Ipv4Addr> for AlternativeSmfIpAddress {
fn from(addr: Ipv4Addr) -> Self {
AlternativeSmfIpAddress::new_ipv4(addr)
}
}
impl From<Ipv6Addr> for AlternativeSmfIpAddress {
fn from(addr: Ipv6Addr) -> Self {
AlternativeSmfIpAddress::new_ipv6(addr)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_alternative_smf_ip_address_ipv4_only() {
let addr = Ipv4Addr::new(192, 168, 1, 100);
let alt_smf = AlternativeSmfIpAddress::new_ipv4(addr);
assert_eq!(alt_smf.ipv4_address, Some(addr));
assert_eq!(alt_smf.ipv6_address, None);
assert!(!alt_smf.preferred_pfcp_entity);
let marshaled = alt_smf.marshal();
let expected = vec![0x02, 192, 168, 1, 100]; assert_eq!(marshaled, expected);
let unmarshaled = AlternativeSmfIpAddress::unmarshal(&marshaled).unwrap();
assert_eq!(unmarshaled, alt_smf);
}
#[test]
fn test_alternative_smf_ip_address_ipv6_only() {
let addr = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 1);
let alt_smf = AlternativeSmfIpAddress::new_ipv6(addr);
assert_eq!(alt_smf.ipv4_address, None);
assert_eq!(alt_smf.ipv6_address, Some(addr));
let marshaled = alt_smf.marshal();
assert_eq!(marshaled[0], 0x01); assert_eq!(marshaled.len(), 17);
let unmarshaled = AlternativeSmfIpAddress::unmarshal(&marshaled).unwrap();
assert_eq!(unmarshaled, alt_smf);
}
#[test]
fn test_alternative_smf_ip_address_dual_stack() {
let ipv4 = Ipv4Addr::new(10, 0, 0, 1);
let ipv6 = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
let alt_smf = AlternativeSmfIpAddress::new_dual_stack(ipv4, ipv6);
assert_eq!(alt_smf.ipv4_address, Some(ipv4));
assert_eq!(alt_smf.ipv6_address, Some(ipv6));
let marshaled = alt_smf.marshal();
assert_eq!(marshaled[0], 0x03); assert_eq!(marshaled.len(), 21);
let unmarshaled = AlternativeSmfIpAddress::unmarshal(&marshaled).unwrap();
assert_eq!(unmarshaled, alt_smf);
}
#[test]
fn test_alternative_smf_ip_address_with_ppe_flag() {
let addr = Ipv4Addr::new(203, 0, 113, 1);
let alt_smf = AlternativeSmfIpAddress::new_ipv4(addr).with_preferred_pfcp_entity(true);
assert!(alt_smf.preferred_pfcp_entity);
let marshaled = alt_smf.marshal();
assert_eq!(marshaled[0], 0x06);
let unmarshaled = AlternativeSmfIpAddress::unmarshal(&marshaled).unwrap();
assert_eq!(unmarshaled, alt_smf);
assert!(unmarshaled.preferred_pfcp_entity);
}
#[test]
fn test_alternative_smf_ip_address_to_ie() {
let addr = Ipv4Addr::new(203, 0, 113, 1);
let alt_smf = AlternativeSmfIpAddress::new_ipv4(addr);
let ie = alt_smf.to_ie();
assert_eq!(ie.ie_type, IeType::AlternativeSmfIpAddress);
assert_eq!(ie.payload, alt_smf.marshal());
}
#[test]
fn test_alternative_smf_ip_address_unmarshal_errors() {
let result = AlternativeSmfIpAddress::unmarshal(&[]);
assert!(result.is_err());
let result = AlternativeSmfIpAddress::unmarshal(&[0x00]);
assert!(result.is_err());
let result = AlternativeSmfIpAddress::unmarshal(&[0x08]); assert!(result.is_err());
let result = AlternativeSmfIpAddress::unmarshal(&[0x02, 192, 168]);
assert!(result.is_err());
let result = AlternativeSmfIpAddress::unmarshal(&[0x01, 0x20, 0x01]);
assert!(result.is_err());
let result = AlternativeSmfIpAddress::unmarshal(&[0x03, 192, 168, 1, 1, 0x20, 0x01]);
assert!(result.is_err());
}
#[test]
fn test_alternative_smf_ip_address_spec_compliance() {
let ipv4 = Ipv4Addr::new(192, 168, 1, 1);
let ipv6 = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 1);
let alt_smf_v4 = AlternativeSmfIpAddress::new_ipv4(ipv4);
let marshaled = alt_smf_v4.marshal();
assert_eq!(marshaled[0], 0x02);
let alt_smf_v6 = AlternativeSmfIpAddress::new_ipv6(ipv6);
let marshaled = alt_smf_v6.marshal();
assert_eq!(marshaled[0], 0x01);
let alt_smf_dual = AlternativeSmfIpAddress::new_dual_stack(ipv4, ipv6);
let marshaled = alt_smf_dual.marshal();
assert_eq!(marshaled[0], 0x03);
let alt_smf_v4_ppe =
AlternativeSmfIpAddress::new_ipv4(ipv4).with_preferred_pfcp_entity(true);
let marshaled = alt_smf_v4_ppe.marshal();
assert_eq!(marshaled[0], 0x06);
let alt_smf_all =
AlternativeSmfIpAddress::new_dual_stack(ipv4, ipv6).with_preferred_pfcp_entity(true);
let marshaled = alt_smf_all.marshal();
assert_eq!(marshaled[0], 0x07); }
#[test]
fn test_alternative_smf_ip_address_round_trip() {
let test_cases = vec![
AlternativeSmfIpAddress::new_ipv4(Ipv4Addr::new(127, 0, 0, 1)),
AlternativeSmfIpAddress::new_ipv6(Ipv6Addr::LOCALHOST),
AlternativeSmfIpAddress::new_dual_stack(
Ipv4Addr::new(10, 0, 0, 1),
Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
),
AlternativeSmfIpAddress::new_ipv4(Ipv4Addr::new(203, 0, 113, 5))
.with_preferred_pfcp_entity(true),
AlternativeSmfIpAddress::new_dual_stack(
Ipv4Addr::new(198, 51, 100, 1),
Ipv6Addr::new(0x2001, 0x0db8, 0x85a3, 0, 0, 0x8a2e, 0x0370, 0x7334),
)
.with_preferred_pfcp_entity(true),
];
for addr in test_cases {
let marshaled = addr.marshal();
let unmarshaled = AlternativeSmfIpAddress::unmarshal(&marshaled).unwrap();
assert_eq!(addr, unmarshaled);
}
}
}