use pnet::util::MacAddr;
use pnet_layers::Layers;
use serde::{Deserialize, Serialize};
use std::{net::Ipv4Addr, str::FromStr};
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub struct RulePacket {
pub eth_saddr: Rpv<String>,
pub eth_daddr: Rpv<String>,
pub eth_type: Rpv<u16>,
pub eth_payload: EthPayload,
}
pub enum RuleLayer {
Ether(RulePacket),
Vlan(RuleVlan),
Ipv4(RuleIpv4),
Udp(RuleUpd),
Tcp(RuleTcp),
Payload(Payload),
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub enum Rpv<T: Clone> {
#[default]
Any,
Equal(T),
NotEqual(T),
GreaterThan(T),
Contains(Vec<T>),
NotContains(Vec<T>),
Multiple(Vec<Rpv<T>>),
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default)]
pub enum RpvType {
#[default]
Any,
Equal,
NotEqual,
GreaterThan,
Contains,
NotContains,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub enum EthPayload {
#[default]
Any,
Vlan(RuleVlan),
Ipv4(RuleIpv4),
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub struct RuleVlan {
pub id: Rpv<u16>,
pub payload: VlanPayload,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
#[allow(clippy::large_enum_variant)]
pub enum VlanPayload {
#[default]
Any,
Ipv4(RuleIpv4),
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub struct RuleIpv4 {
pub mf: Rpv<bool>,
pub fragment_offset: Rpv<usize>,
pub ihl: Rpv<usize>,
pub protocol: Rpv<u16>,
pub daddr: Rpv<Ipv4Address>,
pub saddr: Rpv<Ipv4Address>,
pub payload: Ipv4Payload,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub enum Ipv4Payload {
#[default]
Any,
Udp(RuleUpd),
Tcp(RuleTcp),
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub struct RuleUpd {
pub sport: Rpv<Port>,
pub dport: Rpv<Port>,
pub payload: Payload,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub struct RuleTcp {
pub sport: Rpv<Port>,
pub dport: Rpv<Port>,
pub payload: Payload,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Default, Hash)]
pub enum Payload {
#[default]
Any,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Hash)]
pub enum Port {
Range(u16, u16),
Single(u16),
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Clone, Deserialize, Hash)]
pub enum Ipv4Address {
Range(String, String),
Single(String),
}
impl<T: Clone + std::fmt::Debug> Rpv<T> {
pub fn is_any(&self) -> bool {
matches!(self, Self::Any)
}
}
impl RulePacket {
#[must_use]
pub fn get_layer(&self, layer: &Layers) -> Option<RuleLayer> {
if matches!(layer, Layers::Ether) {
return Some(RuleLayer::Ether(self.to_owned()));
}
let payload = &self.eth_payload;
let ipv4 = match &payload {
EthPayload::Any => return None,
EthPayload::Ipv4(ipv4) => ipv4,
EthPayload::Vlan(vlan) => {
if matches!(layer, Layers::Vlan) {
return Some(RuleLayer::Vlan(vlan.to_owned()));
}
match &vlan.payload {
VlanPayload::Any => return None,
VlanPayload::Ipv4(ipv4) => ipv4,
}
}
};
if matches!(layer, Layers::Ipv4) {
return Some(RuleLayer::Ipv4(ipv4.to_owned()));
}
let payload = match &ipv4.payload {
Ipv4Payload::Any => return None,
Ipv4Payload::Tcp(tcp) => {
if matches!(layer, Layers::Tcp) {
return Some(RuleLayer::Tcp(tcp.to_owned()));
}
&tcp.payload
}
Ipv4Payload::Udp(udp) => {
if matches!(layer, Layers::Udp) {
return Some(RuleLayer::Udp(udp.to_owned()));
}
&udp.payload
}
};
if matches!(layer, Layers::Payload) {
return Some(RuleLayer::Payload(payload.to_owned()));
}
None
}
}
impl Rpv<String> {
#[must_use]
pub fn as_mac_addr(&self) -> Option<MacAddr> {
match self {
Rpv::Any | Rpv::NotEqual(_) => MacAddr::from_str("3c:ce:33:33:33:33").ok(),
Rpv::Equal(a) => MacAddr::from_str(a).ok(),
Rpv::Contains(a) => MacAddr::from_str(&a[0]).ok(),
_ => {
log::error!("{self:?} not allowed");
None
}
}
}
}
impl Rpv<Port> {
#[must_use]
pub fn as_port(&self) -> Option<u16> {
match self {
Rpv::Any => Some(9999),
Rpv::Equal(Port::Single(a)) => Some(*a),
Rpv::Contains(ports) => {
for a in ports {
if let Port::Range(a, _b) = a {
return Some(*a + 1);
}
if let Port::Single(a) = a {
return Some(*a);
}
}
log::error!("Error contains in ipv4 not found");
None
}
_ => todo!("{self:?} not allowed as port"),
}
}
}
impl Rpv<Ipv4Address> {
#[must_use]
pub fn as_ipv4(&self) -> Option<Ipv4Addr> {
match self {
Rpv::Any => Some(Ipv4Addr::new(99, 99, 99, 99)),
Rpv::Equal(Ipv4Address::Single(a)) => Ipv4Addr::from_str(a).ok(),
Rpv::Contains(list) => {
for a in list {
if let Ipv4Address::Range(a, _b) = a {
return Ipv4Addr::from_str(&a.replace('0', "9")).ok();
}
if let Ipv4Address::Single(a) = a {
return Ipv4Addr::from_str(a).ok();
}
}
log::error!("Error contains in ipv4 not found");
None
}
_ => {
log::error!("{self:?} not allowed");
None
}
}
}
}
impl Rpv<u16> {
#[must_use]
pub fn as_u16(&self) -> u16 {
match self {
Rpv::Any => 1,
Rpv::Equal(a) => *a,
Rpv::Contains(a) => a[0],
Rpv::NotContains(a) => a.iter().sum(),
_ => todo!("{:?} not allowed as vlan id", self),
}
}
}
impl Rpv<usize> {
#[must_use]
pub fn as_usize(&self) -> usize {
match self {
Rpv::Any => 1,
Rpv::Equal(a) => *a,
Rpv::Contains(a) => a[0],
Rpv::NotContains(a) => a.iter().sum(),
_ => todo!("{:?} not allowed as vlan id", self),
}
}
}