diff options
Diffstat (limited to 'src/expr/payload.rs')
-rw-r--r-- | src/expr/payload.rs | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/src/expr/payload.rs b/src/expr/payload.rs new file mode 100644 index 0000000..7612fd9 --- /dev/null +++ b/src/expr/payload.rs @@ -0,0 +1,531 @@ +use super::{DeserializationError, Expression, Rule}; +use crate::sys::{self, libc}; +use std::os::raw::c_char; + +pub trait HeaderField { + fn offset(&self) -> u32; + fn len(&self) -> u32; +} + +/// Payload expressions refer to data from the packet's payload. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Payload { + LinkLayer(LLHeaderField), + Network(NetworkHeaderField), + Transport(TransportHeaderField), +} + +impl Payload { + pub fn build(&self) -> RawPayload { + match *self { + Payload::LinkLayer(ref f) => RawPayload::LinkLayer(RawPayloadData { + offset: f.offset(), + len: f.len(), + }), + Payload::Network(ref f) => RawPayload::Network(RawPayloadData { + offset: f.offset(), + len: f.len(), + }), + Payload::Transport(ref f) => RawPayload::Transport(RawPayloadData { + offset: f.offset(), + len: f.offset(), + }), + } + } +} + +impl Expression for Payload { + fn get_raw_name() -> *const libc::c_char { + RawPayload::get_raw_name() + } + + fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { + self.build().to_expr(rule) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct RawPayloadData { + offset: u32, + len: u32, +} + +/// Because deserializing a `Payload` expression is not possible (there is not enough information +/// in the expression itself, this enum should be used to deserialize payloads. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum RawPayload { + LinkLayer(RawPayloadData), + Network(RawPayloadData), + Transport(RawPayloadData), +} + +impl RawPayload { + fn base(&self) -> u32 { + match self { + Self::LinkLayer(_) => libc::NFT_PAYLOAD_LL_HEADER as u32, + Self::Network(_) => libc::NFT_PAYLOAD_NETWORK_HEADER as u32, + Self::Transport(_) => libc::NFT_PAYLOAD_TRANSPORT_HEADER as u32, + } + } +} + +impl HeaderField for RawPayload { + fn offset(&self) -> u32 { + match self { + Self::LinkLayer(ref f) | Self::Network(ref f) | Self::Transport(ref f) => f.offset, + } + } + + fn len(&self) -> u32 { + match self { + Self::LinkLayer(ref f) | Self::Network(ref f) | Self::Transport(ref f) => f.len, + } + } +} + +impl Expression for RawPayload { + fn get_raw_name() -> *const libc::c_char { + b"payload\0" as *const _ as *const c_char + } + + fn from_expr(expr: *const sys::nftnl_expr) -> Result<Self, DeserializationError> { + unsafe { + let base = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_BASE as u16); + let offset = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_OFFSET as u16); + let len = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_LEN as u16); + match base as i32 { + libc::NFT_PAYLOAD_LL_HEADER => Ok(Self::LinkLayer(RawPayloadData { offset, len })), + libc::NFT_PAYLOAD_NETWORK_HEADER => { + Ok(Self::Network(RawPayloadData { offset, len })) + } + libc::NFT_PAYLOAD_TRANSPORT_HEADER => { + Ok(Self::Transport(RawPayloadData { offset, len })) + } + + _ => return Err(DeserializationError::InvalidValue), + } + } + } + + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { + unsafe { + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); + + sys::nftnl_expr_set_u32(expr, sys::NFTNL_EXPR_PAYLOAD_BASE as u16, self.base()); + sys::nftnl_expr_set_u32(expr, sys::NFTNL_EXPR_PAYLOAD_OFFSET as u16, self.offset()); + sys::nftnl_expr_set_u32(expr, sys::NFTNL_EXPR_PAYLOAD_LEN as u16, self.len()); + sys::nftnl_expr_set_u32( + expr, + sys::NFTNL_EXPR_PAYLOAD_DREG as u16, + libc::NFT_REG_1 as u32, + ); + + expr + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum LLHeaderField { + Daddr, + Saddr, + EtherType, +} + +impl HeaderField for LLHeaderField { + fn offset(&self) -> u32 { + use self::LLHeaderField::*; + match *self { + Daddr => 0, + Saddr => 6, + EtherType => 12, + } + } + + fn len(&self) -> u32 { + use self::LLHeaderField::*; + match *self { + Daddr => 6, + Saddr => 6, + EtherType => 2, + } + } +} + +impl LLHeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Result<Self, DeserializationError> { + let off = data.offset; + let len = data.len; + + if off == 0 && len == 6 { + Ok(Self::Daddr) + } else if off == 6 && len == 6 { + Ok(Self::Saddr) + } else if off == 12 && len == 2 { + Ok(Self::EtherType) + } else { + Err(DeserializationError::InvalidValue) + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum NetworkHeaderField { + Ipv4(Ipv4HeaderField), + Ipv6(Ipv6HeaderField), +} + +impl HeaderField for NetworkHeaderField { + fn offset(&self) -> u32 { + use self::NetworkHeaderField::*; + match *self { + Ipv4(ref f) => f.offset(), + Ipv6(ref f) => f.offset(), + } + } + + fn len(&self) -> u32 { + use self::NetworkHeaderField::*; + match *self { + Ipv4(ref f) => f.len(), + Ipv6(ref f) => f.len(), + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum Ipv4HeaderField { + Ttl, + Protocol, + Saddr, + Daddr, +} + +impl HeaderField for Ipv4HeaderField { + fn offset(&self) -> u32 { + use self::Ipv4HeaderField::*; + match *self { + Ttl => 8, + Protocol => 9, + Saddr => 12, + Daddr => 16, + } + } + + fn len(&self) -> u32 { + use self::Ipv4HeaderField::*; + match *self { + Ttl => 1, + Protocol => 1, + Saddr => 4, + Daddr => 4, + } + } +} + +impl Ipv4HeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Result<Self, DeserializationError> { + let off = data.offset; + let len = data.len; + + if off == 8 && len == 1 { + Ok(Self::Ttl) + } else if off == 9 && len == 1 { + Ok(Self::Protocol) + } else if off == 12 && len == 4 { + Ok(Self::Saddr) + } else if off == 16 && len == 4 { + Ok(Self::Daddr) + } else { + Err(DeserializationError::InvalidValue) + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum Ipv6HeaderField { + NextHeader, + HopLimit, + Saddr, + Daddr, +} + +impl HeaderField for Ipv6HeaderField { + fn offset(&self) -> u32 { + use self::Ipv6HeaderField::*; + match *self { + NextHeader => 6, + HopLimit => 7, + Saddr => 8, + Daddr => 24, + } + } + + fn len(&self) -> u32 { + use self::Ipv6HeaderField::*; + match *self { + NextHeader => 1, + HopLimit => 1, + Saddr => 16, + Daddr => 16, + } + } +} + +impl Ipv6HeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Result<Self, DeserializationError> { + let off = data.offset; + let len = data.len; + + if off == 6 && len == 1 { + Ok(Self::NextHeader) + } else if off == 7 && len == 1 { + Ok(Self::HopLimit) + } else if off == 8 && len == 16 { + Ok(Self::Saddr) + } else if off == 24 && len == 16 { + Ok(Self::Daddr) + } else { + Err(DeserializationError::InvalidValue) + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum TransportHeaderField { + Tcp(TcpHeaderField), + Udp(UdpHeaderField), + Icmpv6(Icmpv6HeaderField), +} + +impl HeaderField for TransportHeaderField { + fn offset(&self) -> u32 { + use self::TransportHeaderField::*; + match *self { + Tcp(ref f) => f.offset(), + Udp(ref f) => f.offset(), + Icmpv6(ref f) => f.offset(), + } + } + + fn len(&self) -> u32 { + use self::TransportHeaderField::*; + match *self { + Tcp(ref f) => f.len(), + Udp(ref f) => f.len(), + Icmpv6(ref f) => f.len(), + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum TcpHeaderField { + Sport, + Dport, +} + +impl HeaderField for TcpHeaderField { + fn offset(&self) -> u32 { + use self::TcpHeaderField::*; + match *self { + Sport => 0, + Dport => 2, + } + } + + fn len(&self) -> u32 { + use self::TcpHeaderField::*; + match *self { + Sport => 2, + Dport => 2, + } + } +} + +impl TcpHeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Result<Self, DeserializationError> { + let off = data.offset; + let len = data.len; + + if off == 0 && len == 2 { + Ok(Self::Sport) + } else if off == 2 && len == 2 { + Ok(Self::Dport) + } else { + Err(DeserializationError::InvalidValue) + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum UdpHeaderField { + Sport, + Dport, + Len, +} + +impl HeaderField for UdpHeaderField { + fn offset(&self) -> u32 { + use self::UdpHeaderField::*; + match *self { + Sport => 0, + Dport => 2, + Len => 4, + } + } + + fn len(&self) -> u32 { + use self::UdpHeaderField::*; + match *self { + Sport => 2, + Dport => 2, + Len => 2, + } + } +} + +impl UdpHeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Result<Self, DeserializationError> { + let off = data.offset; + let len = data.len; + + if off == 0 && len == 2 { + Ok(Self::Sport) + } else if off == 2 && len == 2 { + Ok(Self::Dport) + } else if off == 4 && len == 2 { + Ok(Self::Len) + } else { + Err(DeserializationError::InvalidValue) + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum Icmpv6HeaderField { + Type, + Code, + Checksum, +} + +impl HeaderField for Icmpv6HeaderField { + fn offset(&self) -> u32 { + use self::Icmpv6HeaderField::*; + match *self { + Type => 0, + Code => 1, + Checksum => 2, + } + } + + fn len(&self) -> u32 { + use self::Icmpv6HeaderField::*; + match *self { + Type => 1, + Code => 1, + Checksum => 2, + } + } +} + +impl Icmpv6HeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Result<Self, DeserializationError> { + let off = data.offset; + let len = data.len; + + if off == 0 && len == 1 { + Ok(Self::Type) + } else if off == 1 && len == 1 { + Ok(Self::Code) + } else if off == 2 && len == 2 { + Ok(Self::Checksum) + } else { + Err(DeserializationError::InvalidValue) + } + } +} + +#[macro_export(local_inner_macros)] +macro_rules! nft_expr_payload { + (@ipv4_field ttl) => { + $crate::expr::Ipv4HeaderField::Ttl + }; + (@ipv4_field protocol) => { + $crate::expr::Ipv4HeaderField::Protocol + }; + (@ipv4_field saddr) => { + $crate::expr::Ipv4HeaderField::Saddr + }; + (@ipv4_field daddr) => { + $crate::expr::Ipv4HeaderField::Daddr + }; + + (@ipv6_field nextheader) => { + $crate::expr::Ipv6HeaderField::NextHeader + }; + (@ipv6_field hoplimit) => { + $crate::expr::Ipv6HeaderField::HopLimit + }; + (@ipv6_field saddr) => { + $crate::expr::Ipv6HeaderField::Saddr + }; + (@ipv6_field daddr) => { + $crate::expr::Ipv6HeaderField::Daddr + }; + + (@tcp_field sport) => { + $crate::expr::TcpHeaderField::Sport + }; + (@tcp_field dport) => { + $crate::expr::TcpHeaderField::Dport + }; + + (@udp_field sport) => { + $crate::expr::UdpHeaderField::Sport + }; + (@udp_field dport) => { + $crate::expr::UdpHeaderField::Dport + }; + (@udp_field len) => { + $crate::expr::UdpHeaderField::Len + }; + + (ethernet daddr) => { + $crate::expr::Payload::LinkLayer($crate::expr::LLHeaderField::Daddr) + }; + (ethernet saddr) => { + $crate::expr::Payload::LinkLayer($crate::expr::LLHeaderField::Saddr) + }; + (ethernet ethertype) => { + $crate::expr::Payload::LinkLayer($crate::expr::LLHeaderField::EtherType) + }; + + (ipv4 $field:ident) => { + $crate::expr::Payload::Network($crate::expr::NetworkHeaderField::Ipv4( + nft_expr_payload!(@ipv4_field $field), + )) + }; + (ipv6 $field:ident) => { + $crate::expr::Payload::Network($crate::expr::NetworkHeaderField::Ipv6( + nft_expr_payload!(@ipv6_field $field), + )) + }; + + (tcp $field:ident) => { + $crate::expr::Payload::Transport($crate::expr::TransportHeaderField::Tcp( + nft_expr_payload!(@tcp_field $field), + )) + }; + (udp $field:ident) => { + $crate::expr::Payload::Transport($crate::expr::TransportHeaderField::Udp( + nft_expr_payload!(@udp_field $field), + )) + }; +} |