Skip to content

Commit

Permalink
Merge pull request #5 from jonathan-barda/jonny__support_arp_matching
Browse files Browse the repository at this point in the history
min_shark: Support arp matching; DEV-18699
  • Loading branch information
dovreshef authored Dec 9, 2024
2 parents 981b85f + c0cff68 commit aca905c
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 93 deletions.
16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "min_shark"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
authors = ["Dov Reshef <[email protected]>"]
license = "MIT OR Apache-2.0"
Expand All @@ -17,12 +17,12 @@ exclude = [
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bstr = "1.9"
regex = "1.10"
derive_more = "0.99"
ipnet = "2.9"
memchr = "2.7"
bstr = "1.11"
regex = "1.11.1"
derive_more = { version = "1.0.0", features = ["full"] }
ipnet = "2.10.1"
memchr = "2.7.4"

[dev-dependencies]
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
6 changes: 4 additions & 2 deletions docs/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

* tcp: bool
* udp: bool
* vlan: bool
* vlan: bool
* arp: bool
* eth.addr: byte-string | regex
* eth.dst: byte-string | regex
* eth.src: byte-string | regex
Expand All @@ -27,7 +28,8 @@ use the fields name with or without logical operations.
Example:
* 'tcp'
* 'not udp'

* '!arp'

### byte-string

hexadecimal numbers separated by ':'.
Expand Down
3 changes: 1 addition & 2 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
edition = "2021"
version = "Two"
style_edition = "2024"
imports_layout = "Vertical"
imports_granularity = "Crate"
4 changes: 2 additions & 2 deletions src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
//! * Parse the list of tokens into an Expression object, or return the parsing error.
//! * For errors, try to figure out the root and to present it in a human friendly manner.
use crate::{
Expression,
input::Input,
lexer::{
Lexer,
Token,
TokenKind,
},
parser::Parser,
Expression,
};

/// What we expected to find where we found the error
Expand Down Expand Up @@ -152,6 +152,7 @@ pub fn parse(filter_expr: &str) -> Result<Expression, ParseError> {
mod tests {
use super::parse;
use crate::{
Expression,
driver::ErrorKind,
expression::{
Clause,
Expand All @@ -164,7 +165,6 @@ mod tests {
},
mac_addr::MacAddr,
test_utils::init_test_logging,
Expression,
};
use regex::bytes::Regex;
use tracing::info;
Expand Down
110 changes: 74 additions & 36 deletions src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,32 @@ impl Eq for RegexMatcher {}
/// List of the supported comparison operations
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum CmpOp {
#[display(fmt = "==")]
#[display("==")]
Equal,
#[display(fmt = "!=")]
#[display("!=")]
NotEqual,
#[display(fmt = "<")]
#[display("<")]
LessThan,
#[display(fmt = "<=")]
#[display("<=")]
LessEqual,
#[display(fmt = ">")]
#[display(">")]
GreaterThan,
#[display(fmt = ">=")]
#[display(">=")]
GreaterEqual,
}

/// List of supported ethernet operations
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum EthOp {
#[display(fmt = "{op} {val}")]
#[display("{op} {val}")]
Compare { op: CmpOp, val: MacAddr },
#[display(fmt = "in {_0:?}")]
#[display("in {_0:?}")]
MatchAny(Vec<MacAddr>),
#[display(fmt = "not in {_0:?}")]
#[display("not in {_0:?}")]
MatchNone(Vec<MacAddr>),
#[display(fmt = "contains {_0:?}")]
#[display("contains {_0:?}")]
Contains(Vec<u8>),
#[display(fmt = "matches {_0}")]
#[display("matches {_0}")]
RegexMatch(RegexMatcher),
}

Expand Down Expand Up @@ -108,11 +108,11 @@ impl EthOp {
/// List of supported IP operations
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum IpOp {
#[display(fmt = "{op} {val}")]
#[display("{op} {val}")]
Compare { op: CmpOp, val: IpNet },
#[display(fmt = "in {_0:?}")]
#[display("in {_0:?}")]
MatchAny(Vec<IpNet>),
#[display(fmt = "not in {_0:?}")]
#[display("not in {_0:?}")]
MatchNone(Vec<IpNet>),
}

Expand Down Expand Up @@ -151,11 +151,11 @@ impl IpOp {
/// List of supported Port operations
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum ValOp {
#[display(fmt = "{op} {val}")]
#[display("{op} {val}")]
Compare { op: CmpOp, val: u32 },
#[display(fmt = "in {_0:?}")]
#[display("in {_0:?}")]
MatchAny(Vec<u32>),
#[display(fmt = "not in {_0:?}")]
#[display("not in {_0:?}")]
MatchNone(Vec<u32>),
}

Expand Down Expand Up @@ -194,9 +194,9 @@ impl ValOp {
/// List of supported payload operations
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum PayloadOp {
#[display(fmt = "contains {_0:?}")]
#[display("contains {_0:?}")]
Contains(Vec<u8>),
#[display(fmt = "matches {_0}")]
#[display("matches {_0}")]
RegexMatch(RegexMatcher),
}

Expand All @@ -222,7 +222,7 @@ impl PayloadOp {
/// List of supported Port operations
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum PayloadLenOp {
#[display(fmt = "{op} {val}")]
#[display("{op} {val}")]
Compare { op: CmpOp, val: u32 },
}

Expand Down Expand Up @@ -252,49 +252,52 @@ impl PayloadLenOp {
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum Clause {
/// Is Tcp
#[display(fmt = "tcp")]
#[display("tcp")]
IsTcp,
/// Is udp
#[display(fmt = "udp")]
#[display("udp")]
IsUdp,
/// Is vlan
#[display(fmt = "vlan")]
#[display("vlan")]
IsVlan,
/// Match ARP
#[display("arp")]
IsArp,
/// Match any of the vlans
#[display(fmt = "vlan.id {_0}")]
#[display("vlan.id {_0}")]
VlanId(ValOp),
/// Match any of destination ports
#[display(fmt = "dstport {_0}")]
#[display("dstport {_0}")]
PortDst(ValOp),
/// Match any of source ports
#[display(fmt = "srcport {_0}")]
#[display("srcport {_0}")]
PortSrc(ValOp),
/// Match any of either the source or destination ports
#[display(fmt = "port {_0}")]
#[display("port {_0}")]
Port(ValOp),
/// Ethernet destination match
#[display(fmt = "eth.dst {_0}")]
#[display("eth.dst {_0}")]
EthDst(EthOp),
/// Ethernet source match
#[display(fmt = "eth.src {_0}")]
#[display("eth.src {_0}")]
EthSrc(EthOp),
/// Ethernet either source or destination match
#[display(fmt = "eth {_0}")]
#[display("eth {_0}")]
EthAddr(EthOp),
/// Destination IP match
#[display(fmt = "ip.dst {_0}")]
#[display("ip.dst {_0}")]
IpDst(IpOp),
/// Source IP match
#[display(fmt = "ip.src {_0}")]
#[display("ip.src {_0}")]
IpSrc(IpOp),
/// Either source or destination IP
#[display(fmt = "ip {_0}")]
#[display("ip {_0}")]
IpAddr(IpOp),
/// Match payload
#[display(fmt = "payload {_0}")]
#[display("payload {_0}")]
Payload(PayloadOp),
/// Match payload length
#[display(fmt = "payload.len {_0}")]
#[display("payload.len {_0}")]
PayloadLen(PayloadLenOp),
}

Expand All @@ -305,6 +308,7 @@ impl Clause {
Clause::IsTcp => matcher.is_tcp.unwrap_or_default(),
Clause::IsUdp => matcher.is_udp.unwrap_or_default(),
Clause::IsVlan => matcher.is_vlan.unwrap_or_default(),
Clause::IsArp => matcher.is_arp.unwrap_or_default(),
Clause::VlanId(vlan_op) => matcher
.vlan
.map(|v| vlan_op.is_match(v))
Expand Down Expand Up @@ -477,6 +481,7 @@ impl Expression {
is_tcp: None,
is_udp: None,
is_vlan: None,
is_arp: None,
src_eth: None,
dst_eth: None,
src_ip: None,
Expand Down Expand Up @@ -507,6 +512,7 @@ pub struct Matcher<'e, 'p> {
is_tcp: Option<bool>,
is_udp: Option<bool>,
is_vlan: Option<bool>,
is_arp: Option<bool>,
src_eth: Option<MacAddr>,
dst_eth: Option<MacAddr>,
src_ip: Option<IpNet>,
Expand All @@ -517,7 +523,7 @@ pub struct Matcher<'e, 'p> {
payload: Option<&'p [u8]>,
}

impl<'e, 'p> Matcher<'e, 'p> {
impl<'p> Matcher<'_, 'p> {
/// Whether the packet has tcp data
pub fn tcp(mut self, val: bool) -> Self {
self.is_tcp = Some(val);
Expand All @@ -537,6 +543,12 @@ impl<'e, 'p> Matcher<'e, 'p> {
self
}

/// Whether the packet has arp data
pub fn arp(mut self, val: bool) -> Self {
self.is_arp = Some(val);
self
}

/// The source ethernet address
pub fn src_eth(mut self, val: MacAddr) -> Self {
self.src_eth = Some(val);
Expand Down Expand Up @@ -805,6 +817,32 @@ mod tests {
assert!(res);
}

#[test]
fn test_single_clause_arp_expression() {
init_test_logging();

for (exp, is_arp, expected) in [
(Expression::from(Clause::IsArp), true, true),
(Expression::from(Clause::IsArp), false, false),
(Expression::not(Clause::IsArp), true, false),
(Expression::not(Clause::IsArp), false, true),
] {
info!("Evaluating expression \"{exp}\"");
let res = exp.matcher().arp(is_arp).is_match();
assert_eq!(res, expected);
}
}

#[test]
fn test_complex_not_arp_expression() {
init_test_logging();
let expression =
Expression::not(Clause::IsArp).and(Clause::Port(ValOp::match_any(vec![80, 443])));
info!("Evaluating expression \"{expression}\"");
let res = expression.matcher().arp(false).srcport(80).is_match();
assert!(res);
}

#[test]
fn test_single_clause_vlan_expressions() {
init_test_logging();
Expand Down
Loading

0 comments on commit aca905c

Please sign in to comment.