use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::hash::{Hash, Hasher};
use std::net::{Ipv4Addr, Ipv6Addr};
use crate::packet::ether::EthAddr;
use crate::packet::protocol::{EtherProto, IpProto};
use crate::{
packet::{
header::{LinkLayer, NetworkLayer},
Packet,
},
tracker::vni::{VniId, VniLayer, VniMapper},
};
pub trait Tuple: Sized + Hash + Eq + Clone + Copy {
type Addr: Eq;
fn from_packet(pkt: &Packet<'_>, vni_mapper: &mut VniMapper) -> Option<Self>;
fn flip(&self) -> Self;
fn hash_canonical<H: Hasher>(&self, state: &mut H);
fn eq_canonical(&self, other: &Self) -> bool;
#[inline]
fn is_symmetric(&self) -> bool {
self.source_port() == self.dest_port() && self.source() == self.dest()
}
fn source(&self) -> Self::Addr;
fn dest(&self) -> Self::Addr;
fn source_port(&self) -> u16;
fn dest_port(&self) -> u16;
fn protocol(&self) -> IpProto;
fn vni(&self) -> VniId;
}
#[inline]
fn extract_vni(pkt: &Packet<'_>, vni_mapper: &mut VniMapper) -> Option<VniId> {
let network_tunnel_layers = pkt.tunnels();
if network_tunnel_layers.is_empty() {
return Some(VniId::default());
}
let vni_stack = network_tunnel_layers
.iter()
.map(TryInto::try_into)
.collect::<Result<SmallVec<[VniLayer; 4]>, _>>()
.ok()?;
Some(vni_mapper.get_or_create_vni_id(&vni_stack))
}
#[inline]
fn extract_ports(pkt: &Packet<'_>) -> (u16, u16) {
pkt.transport().map(|t| t.ports()).unwrap_or((0, 0))
}
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct Symmetric<T: Tuple>(pub T);
impl<T: Tuple> PartialEq for Symmetric<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0.eq_canonical(&other.0)
}
}
impl<T: Tuple> Eq for Symmetric<T> {}
impl<T: Tuple> Hash for Symmetric<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash_canonical(state);
}
}
impl<T: Tuple> From<T> for Symmetric<T> {
fn from(t: T) -> Self {
Self(t)
}
}
#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone, Serialize, Deserialize)]
pub struct TupleV4 {
pub src_ip: Ipv4Addr,
pub dst_ip: Ipv4Addr,
pub src_port: u16,
pub dst_port: u16,
pub protocol: IpProto,
pub vni: VniId,
}
impl Default for TupleV4 {
fn default() -> Self {
Self {
src_ip: Ipv4Addr::UNSPECIFIED,
dst_ip: Ipv4Addr::UNSPECIFIED,
src_port: 0,
dst_port: 0,
protocol: IpProto::default(),
vni: VniId::default(),
}
}
}
impl TupleV4 {
pub fn new(pkt: &Packet<'_>, vni_mapper: &mut VniMapper) -> Option<Self> {
let NetworkLayer::Ipv4(ipv4) = pkt.network()? else {
return None;
};
let src_ip = ipv4.header.src_ip();
let dst_ip = ipv4.header.dst_ip();
let protocol = ipv4.header.protocol();
let (src_port, dst_port) = extract_ports(pkt);
let vni = extract_vni(pkt, vni_mapper)?;
Some(Self {
src_ip,
dst_ip,
src_port,
dst_port,
protocol,
vni,
})
}
}
impl Tuple for TupleV4 {
type Addr = Ipv4Addr;
#[inline]
fn source(&self) -> Self::Addr {
self.src_ip
}
#[inline]
fn dest(&self) -> Self::Addr {
self.dst_ip
}
#[inline]
fn source_port(&self) -> u16 {
self.src_port
}
#[inline]
fn dest_port(&self) -> u16 {
self.dst_port
}
#[inline]
fn protocol(&self) -> IpProto {
self.protocol
}
#[inline]
fn vni(&self) -> VniId {
self.vni
}
#[inline]
fn from_packet(pkt: &Packet<'_>, vni_mapper: &mut VniMapper) -> Option<Self> {
Self::new(pkt, vni_mapper)
}
#[inline]
fn flip(&self) -> Self {
Self {
src_ip: self.dst_ip,
dst_ip: self.src_ip,
src_port: self.dst_port,
dst_port: self.src_port,
protocol: self.protocol,
vni: self.vni,
}
}
#[inline]
fn hash_canonical<H: Hasher>(&self, state: &mut H) {
self.protocol.hash(state);
self.vni.hash(state);
if (self.src_ip, self.src_port) <= (self.dst_ip, self.dst_port) {
self.src_ip.hash(state);
self.src_port.hash(state);
self.dst_ip.hash(state);
self.dst_port.hash(state);
} else {
self.dst_ip.hash(state);
self.dst_port.hash(state);
self.src_ip.hash(state);
self.src_port.hash(state);
}
}
#[inline]
fn eq_canonical(&self, other: &Self) -> bool {
if self.protocol != other.protocol || self.vni != other.vni {
return false;
}
(self.src_ip == other.src_ip
&& self.dst_ip == other.dst_ip
&& self.src_port == other.src_port
&& self.dst_port == other.dst_port)
|| (self.src_ip == other.dst_ip
&& self.dst_ip == other.src_ip
&& self.src_port == other.dst_port
&& self.dst_port == other.src_port)
}
}
#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone, Serialize, Deserialize)]
pub struct TupleV6 {
pub src_ip: Ipv6Addr,
pub dst_ip: Ipv6Addr,
pub src_port: u16,
pub dst_port: u16,
pub protocol: IpProto,
pub vni: VniId,
}
impl Default for TupleV6 {
fn default() -> Self {
Self {
src_ip: Ipv6Addr::UNSPECIFIED,
dst_ip: Ipv6Addr::UNSPECIFIED,
src_port: 0,
dst_port: 0,
protocol: IpProto::default(),
vni: VniId::default(),
}
}
}
impl TupleV6 {
pub fn new(pkt: &Packet<'_>, vni_mapper: &mut VniMapper) -> Option<Self> {
let NetworkLayer::Ipv6(ipv6) = pkt.network()? else {
return None;
};
let src_ip = ipv6.header.src_ip();
let dst_ip = ipv6.header.dst_ip();
let protocol = ipv6.header.next_header();
let (src_port, dst_port) = extract_ports(pkt);
let vni = extract_vni(pkt, vni_mapper)?;
Some(Self {
src_ip,
dst_ip,
src_port,
dst_port,
protocol,
vni,
})
}
}
impl Tuple for TupleV6 {
type Addr = Ipv6Addr;
#[inline]
fn source(&self) -> Self::Addr {
self.src_ip
}
#[inline]
fn dest(&self) -> Self::Addr {
self.dst_ip
}
#[inline]
fn source_port(&self) -> u16 {
self.src_port
}
#[inline]
fn dest_port(&self) -> u16 {
self.dst_port
}
#[inline]
fn protocol(&self) -> IpProto {
self.protocol
}
#[inline]
fn vni(&self) -> VniId {
self.vni
}
#[inline]
fn from_packet(pkt: &Packet<'_>, vni_mapper: &mut VniMapper) -> Option<Self> {
Self::new(pkt, vni_mapper)
}
#[inline]
fn flip(&self) -> Self {
Self {
src_ip: self.dst_ip,
dst_ip: self.src_ip,
src_port: self.dst_port,
dst_port: self.src_port,
protocol: self.protocol,
vni: self.vni,
}
}
#[inline]
fn hash_canonical<H: Hasher>(&self, state: &mut H) {
self.protocol.hash(state);
self.vni.hash(state);
if (self.src_ip, self.src_port) <= (self.dst_ip, self.dst_port) {
self.src_ip.hash(state);
self.src_port.hash(state);
self.dst_ip.hash(state);
self.dst_port.hash(state);
} else {
self.dst_ip.hash(state);
self.dst_port.hash(state);
self.src_ip.hash(state);
self.src_port.hash(state);
}
}
#[inline]
fn eq_canonical(&self, other: &Self) -> bool {
if self.protocol != other.protocol || self.vni != other.vni {
return false;
}
(self.src_ip == other.src_ip
&& self.dst_ip == other.dst_ip
&& self.src_port == other.src_port
&& self.dst_port == other.dst_port)
|| (self.src_ip == other.dst_ip
&& self.dst_ip == other.src_ip
&& self.src_port == other.dst_port
&& self.dst_port == other.src_port)
}
}
#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone, Serialize, Deserialize)]
pub struct TupleEth {
pub src: EthAddr,
pub dst: EthAddr,
pub protocol: EtherProto,
}
impl Default for TupleEth {
fn default() -> Self {
Self {
src: EthAddr::default(),
dst: EthAddr::default(),
protocol: EtherProto::default(),
}
}
}
impl TupleEth {
pub fn new(pkt: &Packet<'_>) -> Option<Self> {
let LinkLayer::Ethernet(eth) = pkt.link() else {
return None;
};
Some(Self {
src: *eth.source(),
dst: *eth.dest(),
protocol: eth.inner_type(),
})
}
}
impl Tuple for TupleEth {
type Addr = EthAddr;
#[inline]
fn source(&self) -> Self::Addr {
self.src
}
#[inline]
fn dest(&self) -> Self::Addr {
self.dst
}
#[inline]
fn source_port(&self) -> u16 {
0
}
#[inline]
fn dest_port(&self) -> u16 {
0
}
#[inline]
fn protocol(&self) -> IpProto {
IpProto::default()
}
#[inline]
fn vni(&self) -> VniId {
VniId::default()
}
#[inline]
fn from_packet(pkt: &Packet<'_>, _vni_mapper: &mut VniMapper) -> Option<Self> {
Self::new(pkt)
}
#[inline]
fn flip(&self) -> Self {
Self {
src: self.dst,
dst: self.src,
protocol: self.protocol,
}
}
#[inline]
fn hash_canonical<H: Hasher>(&self, state: &mut H) {
self.protocol.hash(state);
if self.src <= self.dst {
self.src.hash(state);
self.dst.hash(state);
} else {
self.dst.hash(state);
self.src.hash(state);
}
}
#[inline]
fn eq_canonical(&self, other: &Self) -> bool {
if self.protocol != other.protocol {
return false;
}
(self.src == other.src && self.dst == other.dst)
|| (self.src == other.dst && self.dst == other.src)
}
}