use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TraceInformation {
pub mcc_mnc: [u8; 3], pub trace_id: [u8; 3], pub triggering_events: Vec<u8>, pub trace_depth: u8, pub list_of_interfaces: Vec<u8>, pub ip_address_of_trace_collection_entity: Option<Vec<u8>>, }
impl TraceInformation {
pub fn new(
mcc_mnc: [u8; 3],
trace_id: [u8; 3],
triggering_events: Vec<u8>,
trace_depth: u8,
list_of_interfaces: Vec<u8>,
) -> Self {
TraceInformation {
mcc_mnc,
trace_id,
triggering_events,
trace_depth,
list_of_interfaces,
ip_address_of_trace_collection_entity: None,
}
}
pub fn with_trace_collection_entity_ip(mut self, ip_address: Vec<u8>) -> Self {
self.ip_address_of_trace_collection_entity = Some(ip_address);
self
}
pub fn with_trace_collection_entity_ipv4(mut self, ipv4: std::net::Ipv4Addr) -> Self {
self.ip_address_of_trace_collection_entity = Some(ipv4.octets().to_vec());
self
}
pub fn with_trace_collection_entity_ipv6(mut self, ipv6: std::net::Ipv6Addr) -> Self {
self.ip_address_of_trace_collection_entity = Some(ipv6.octets().to_vec());
self
}
pub fn plmn_id(&self) -> [u8; 3] {
self.mcc_mnc
}
pub fn trace_id(&self) -> [u8; 3] {
self.trace_id
}
pub fn trace_collection_entity_ipv4(&self) -> Option<std::net::Ipv4Addr> {
if let Some(ref ip) = self.ip_address_of_trace_collection_entity {
if ip.len() == 4 {
return Some(std::net::Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]));
}
}
None
}
pub fn trace_collection_entity_ipv6(&self) -> Option<std::net::Ipv6Addr> {
if let Some(ref ip) = self.ip_address_of_trace_collection_entity {
if ip.len() == 16 {
let mut octets = [0u8; 16];
octets.copy_from_slice(ip);
return Some(std::net::Ipv6Addr::from(octets));
}
}
None
}
pub fn len(&self) -> usize {
let mut len = 3 + 3 + 1 + 1; len += self.triggering_events.len();
len += 1; len += self.list_of_interfaces.len();
if let Some(ref ip) = self.ip_address_of_trace_collection_entity {
len += 1 + ip.len(); } else {
len += 1; }
len
}
pub fn is_empty(&self) -> bool {
false }
pub fn marshal(&self) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&self.mcc_mnc);
data.extend_from_slice(&self.trace_id);
data.push(self.triggering_events.len() as u8);
data.extend_from_slice(&self.triggering_events);
data.push(self.trace_depth);
data.push(self.list_of_interfaces.len() as u8);
data.extend_from_slice(&self.list_of_interfaces);
if let Some(ref ip) = self.ip_address_of_trace_collection_entity {
data.push(ip.len() as u8);
data.extend_from_slice(ip);
} else {
data.push(0); }
data
}
pub fn unmarshal(payload: &[u8]) -> Result<Self, PfcpError> {
if payload.len() < 9 {
return Err(PfcpError::invalid_length(
"Trace Information",
IeType::TraceInformation,
9,
payload.len(),
));
}
let mut offset = 0;
let mut mcc_mnc = [0u8; 3];
mcc_mnc.copy_from_slice(&payload[offset..offset + 3]);
offset += 3;
let mut trace_id = [0u8; 3];
trace_id.copy_from_slice(&payload[offset..offset + 3]);
offset += 3;
let triggering_events_len = payload[offset] as usize;
offset += 1;
if offset + triggering_events_len > payload.len() {
return Err(PfcpError::invalid_value(
"triggering_events_len",
triggering_events_len.to_string(),
"length exceeds remaining payload",
));
}
let triggering_events = payload[offset..offset + triggering_events_len].to_vec();
offset += triggering_events_len;
if offset >= payload.len() {
return Err(PfcpError::invalid_length(
"Trace Information (trace_depth)",
IeType::TraceInformation,
offset + 1,
payload.len(),
));
}
let trace_depth = payload[offset];
offset += 1;
if offset >= payload.len() {
return Err(PfcpError::invalid_length(
"Trace Information (interfaces_len)",
IeType::TraceInformation,
offset + 1,
payload.len(),
));
}
let interfaces_len = payload[offset] as usize;
offset += 1;
if offset + interfaces_len > payload.len() {
return Err(PfcpError::invalid_value(
"interfaces_len",
interfaces_len.to_string(),
"length exceeds remaining payload",
));
}
let list_of_interfaces = payload[offset..offset + interfaces_len].to_vec();
offset += interfaces_len;
if offset >= payload.len() {
return Err(PfcpError::invalid_length(
"Trace Information (ip_len)",
IeType::TraceInformation,
offset + 1,
payload.len(),
));
}
let ip_len = payload[offset] as usize;
offset += 1;
let ip_address_of_trace_collection_entity = if ip_len > 0 {
if offset + ip_len > payload.len() {
return Err(PfcpError::invalid_value(
"ip_len",
ip_len.to_string(),
"length exceeds remaining payload",
));
}
Some(payload[offset..offset + ip_len].to_vec())
} else {
None
};
Ok(TraceInformation {
mcc_mnc,
trace_id,
triggering_events,
trace_depth,
list_of_interfaces,
ip_address_of_trace_collection_entity,
})
}
pub fn to_ie(&self) -> Ie {
Ie::new(IeType::TraceInformation, self.marshal())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{Ipv4Addr, Ipv6Addr};
#[test]
fn test_trace_information_marshal_unmarshal_minimal() {
let mcc_mnc = [0x12, 0x34, 0x56];
let trace_id = [0xAB, 0xCD, 0xEF];
let triggering_events = vec![0x01, 0x02];
let trace_depth = 5;
let list_of_interfaces = vec![0x10, 0x20, 0x30];
let trace_info = TraceInformation::new(
mcc_mnc,
trace_id,
triggering_events.clone(),
trace_depth,
list_of_interfaces.clone(),
);
let marshaled = trace_info.marshal();
let unmarshaled = TraceInformation::unmarshal(&marshaled).unwrap();
assert_eq!(trace_info, unmarshaled);
assert_eq!(unmarshaled.mcc_mnc, mcc_mnc);
assert_eq!(unmarshaled.trace_id, trace_id);
assert_eq!(unmarshaled.triggering_events, triggering_events);
assert_eq!(unmarshaled.trace_depth, trace_depth);
assert_eq!(unmarshaled.list_of_interfaces, list_of_interfaces);
assert_eq!(unmarshaled.ip_address_of_trace_collection_entity, None);
assert!(!trace_info.is_empty());
}
#[test]
fn test_trace_information_marshal_unmarshal_with_ipv4() {
let mcc_mnc = [0x12, 0x34, 0x56];
let trace_id = [0xAB, 0xCD, 0xEF];
let triggering_events = vec![0x01];
let trace_depth = 3;
let list_of_interfaces = vec![0x10];
let ipv4 = Ipv4Addr::new(192, 168, 1, 100);
let trace_info = TraceInformation::new(
mcc_mnc,
trace_id,
triggering_events.clone(),
trace_depth,
list_of_interfaces.clone(),
)
.with_trace_collection_entity_ipv4(ipv4);
let marshaled = trace_info.marshal();
let unmarshaled = TraceInformation::unmarshal(&marshaled).unwrap();
assert_eq!(trace_info, unmarshaled);
assert_eq!(unmarshaled.trace_collection_entity_ipv4(), Some(ipv4));
assert_eq!(unmarshaled.trace_collection_entity_ipv6(), None);
}
#[test]
fn test_trace_information_marshal_unmarshal_with_ipv6() {
let mcc_mnc = [0x12, 0x34, 0x56];
let trace_id = [0xAB, 0xCD, 0xEF];
let triggering_events = vec![0x01, 0x02, 0x03];
let trace_depth = 7;
let list_of_interfaces = vec![0x10, 0x20];
let ipv6 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0x8a2e, 0x370, 0x7334);
let trace_info = TraceInformation::new(
mcc_mnc,
trace_id,
triggering_events.clone(),
trace_depth,
list_of_interfaces.clone(),
)
.with_trace_collection_entity_ipv6(ipv6);
let marshaled = trace_info.marshal();
let unmarshaled = TraceInformation::unmarshal(&marshaled).unwrap();
assert_eq!(trace_info, unmarshaled);
assert_eq!(unmarshaled.trace_collection_entity_ipv6(), Some(ipv6));
assert_eq!(unmarshaled.trace_collection_entity_ipv4(), None);
}
#[test]
fn test_trace_information_with_custom_ip() {
let mcc_mnc = [0x01, 0x02, 0x03];
let trace_id = [0x04, 0x05, 0x06];
let triggering_events = vec![0xFF];
let trace_depth = 1;
let list_of_interfaces = vec![0xAA, 0xBB];
let custom_ip = vec![0x10, 0x20, 0x30, 0x40, 0x50];
let trace_info = TraceInformation::new(
mcc_mnc,
trace_id,
triggering_events.clone(),
trace_depth,
list_of_interfaces.clone(),
)
.with_trace_collection_entity_ip(custom_ip.clone());
let marshaled = trace_info.marshal();
let unmarshaled = TraceInformation::unmarshal(&marshaled).unwrap();
assert_eq!(trace_info, unmarshaled);
assert_eq!(
unmarshaled.ip_address_of_trace_collection_entity,
Some(custom_ip)
);
assert_eq!(unmarshaled.trace_collection_entity_ipv4(), None);
assert_eq!(unmarshaled.trace_collection_entity_ipv6(), None);
}
#[test]
fn test_trace_information_plmn_and_trace_id() {
let mcc_mnc = [0xAA, 0xBB, 0xCC];
let trace_id = [0x11, 0x22, 0x33];
let trace_info = TraceInformation::new(mcc_mnc, trace_id, vec![], 0, vec![]);
assert_eq!(trace_info.plmn_id(), mcc_mnc);
assert_eq!(trace_info.trace_id(), trace_id);
}
#[test]
fn test_trace_information_to_ie() {
let trace_info = TraceInformation::new(
[0x12, 0x34, 0x56],
[0xAB, 0xCD, 0xEF],
vec![0x01],
3,
vec![0x10],
);
let ie = trace_info.to_ie();
assert_eq!(ie.ie_type, IeType::TraceInformation);
let unmarshaled = TraceInformation::unmarshal(&ie.payload).unwrap();
assert_eq!(trace_info, unmarshaled);
}
#[test]
fn test_trace_information_unmarshal_too_short() {
let result = TraceInformation::unmarshal(&[0x01, 0x02]); assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
PfcpError::InvalidLength { .. }
));
}
#[test]
fn test_trace_information_unmarshal_invalid_triggering_events_len() {
let mut payload = vec![0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF]; payload.push(10); payload.extend(vec![0x01, 0x02]);
let result = TraceInformation::unmarshal(&payload);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
PfcpError::InvalidValue { .. }
));
}
#[test]
fn test_trace_information_unmarshal_missing_trace_depth() {
let mut payload = vec![0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF]; payload.push(2); payload.extend(vec![0x01, 0x02]);
let result = TraceInformation::unmarshal(&payload);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
PfcpError::InvalidLength { .. }
));
}
#[test]
fn test_trace_information_len() {
let trace_info_minimal = TraceInformation::new([0; 3], [0; 3], vec![], 0, vec![]);
assert_eq!(trace_info_minimal.len(), 10);
let trace_info_with_data = TraceInformation::new(
[0; 3],
[0; 3],
vec![0x01, 0x02], 0,
vec![0x10, 0x20, 0x30], )
.with_trace_collection_entity_ipv4(Ipv4Addr::new(192, 168, 1, 1));
assert_eq!(trace_info_with_data.len(), 19); }
#[test]
fn test_trace_information_round_trip_empty_lists() {
let trace_info = TraceInformation::new([0xFF; 3], [0xAA; 3], vec![], 255, vec![]);
let marshaled = trace_info.marshal();
let unmarshaled = TraceInformation::unmarshal(&marshaled).unwrap();
assert_eq!(trace_info, unmarshaled);
assert!(unmarshaled.triggering_events.is_empty());
assert!(unmarshaled.list_of_interfaces.is_empty());
assert_eq!(unmarshaled.ip_address_of_trace_collection_entity, None);
}
}