use crate::models::*;
pub mod nlri;
pub mod operators;
#[cfg(test)]
mod tests;
pub use nlri::*;
pub use operators::*;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FlowSpecNlri {
pub components: Vec<FlowSpecComponent>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FlowSpecComponent {
DestinationPrefix(NetworkPrefix),
SourcePrefix(NetworkPrefix),
IpProtocol(Vec<NumericOperator>),
Port(Vec<NumericOperator>),
DestinationPort(Vec<NumericOperator>),
SourcePort(Vec<NumericOperator>),
IcmpType(Vec<NumericOperator>),
IcmpCode(Vec<NumericOperator>),
TcpFlags(Vec<BitmaskOperator>),
PacketLength(Vec<NumericOperator>),
Dscp(Vec<NumericOperator>),
Fragment(Vec<BitmaskOperator>),
FlowLabel(Vec<NumericOperator>),
DestinationIpv6Prefix { offset: u8, prefix: NetworkPrefix },
SourceIpv6Prefix { offset: u8, prefix: NetworkPrefix },
}
impl FlowSpecComponent {
pub const fn component_type(&self) -> u8 {
match self {
FlowSpecComponent::DestinationPrefix(_)
| FlowSpecComponent::DestinationIpv6Prefix { .. } => 1,
FlowSpecComponent::SourcePrefix(_) | FlowSpecComponent::SourceIpv6Prefix { .. } => 2,
FlowSpecComponent::IpProtocol(_) => 3,
FlowSpecComponent::Port(_) => 4,
FlowSpecComponent::DestinationPort(_) => 5,
FlowSpecComponent::SourcePort(_) => 6,
FlowSpecComponent::IcmpType(_) => 7,
FlowSpecComponent::IcmpCode(_) => 8,
FlowSpecComponent::TcpFlags(_) => 9,
FlowSpecComponent::PacketLength(_) => 10,
FlowSpecComponent::Dscp(_) => 11,
FlowSpecComponent::Fragment(_) => 12,
FlowSpecComponent::FlowLabel(_) => 13,
}
}
pub const fn uses_numeric_operators(&self) -> bool {
matches!(
self,
FlowSpecComponent::IpProtocol(_)
| FlowSpecComponent::Port(_)
| FlowSpecComponent::DestinationPort(_)
| FlowSpecComponent::SourcePort(_)
| FlowSpecComponent::IcmpType(_)
| FlowSpecComponent::IcmpCode(_)
| FlowSpecComponent::PacketLength(_)
| FlowSpecComponent::Dscp(_)
| FlowSpecComponent::FlowLabel(_)
)
}
pub const fn uses_bitmask_operators(&self) -> bool {
matches!(
self,
FlowSpecComponent::TcpFlags(_) | FlowSpecComponent::Fragment(_)
)
}
}
impl FlowSpecNlri {
pub fn new(components: Vec<FlowSpecComponent>) -> Self {
FlowSpecNlri { components }
}
pub fn components(&self) -> &[FlowSpecComponent] {
&self.components
}
pub fn is_ipv4(&self) -> bool {
self.components.iter().any(|c| {
matches!(c,
FlowSpecComponent::DestinationPrefix(prefix) |
FlowSpecComponent::SourcePrefix(prefix)
if matches!(prefix.prefix, ipnet::IpNet::V4(_))
)
})
}
pub fn is_ipv6(&self) -> bool {
self.components.iter().any(|c| {
matches!(c,
FlowSpecComponent::DestinationPrefix(prefix) |
FlowSpecComponent::SourcePrefix(prefix)
if matches!(prefix.prefix, ipnet::IpNet::V6(_))
)
}) || self.components.iter().any(|c| {
matches!(
c,
FlowSpecComponent::DestinationIpv6Prefix { .. }
| FlowSpecComponent::SourceIpv6Prefix { .. }
| FlowSpecComponent::FlowLabel(_)
)
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FlowSpecError {
InvalidComponentOrder {
expected_greater_than: u8,
found: u8,
},
InvalidOperator(u8),
InvalidComponentType(u8),
InsufficientData,
InvalidPrefix,
InvalidValueLength(u8),
}
impl std::fmt::Display for FlowSpecError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FlowSpecError::InvalidComponentOrder {
expected_greater_than,
found,
} => {
write!(
f,
"Invalid component order: expected type > {}, but found {}",
expected_greater_than, found
)
}
FlowSpecError::InvalidOperator(op) => {
write!(f, "Invalid operator: 0x{:02X}", op)
}
FlowSpecError::InvalidComponentType(t) => {
write!(f, "Invalid component type: {}", t)
}
FlowSpecError::InsufficientData => {
write!(f, "Insufficient data for parsing")
}
FlowSpecError::InvalidPrefix => {
write!(f, "Invalid prefix encoding")
}
FlowSpecError::InvalidValueLength(len) => {
write!(f, "Invalid value length: {}", len)
}
}
}
}
impl std::error::Error for FlowSpecError {}