1use thiserror::Error;
26
27use crate::filter::{IpNet, PacketMeta, PortRange};
28
29#[derive(Debug, Error)]
33#[error("BPF parse error at column {col}: {message}")]
34pub struct BpfError {
35 pub message: String,
36 pub col: usize,
37}
38
39#[derive(Debug, Clone, PartialEq)]
42enum Tok {
43 Word(String),
44 LParen,
45 RParen,
46 Gt,
47 Lt,
48 Ge,
49 Le,
50 EqEq,
51 Ne,
52}
53
54fn tokenize(input: &str) -> Result<Vec<(Tok, usize)>, BpfError> {
56 let mut out = Vec::new();
57 let b = input.as_bytes();
58 let mut i = 0;
59 while i < b.len() {
60 if b[i].is_ascii_whitespace() {
61 i += 1;
62 continue;
63 }
64 let col = i;
65 match b[i] {
66 b'(' => {
67 out.push((Tok::LParen, col));
68 i += 1;
69 }
70 b')' => {
71 out.push((Tok::RParen, col));
72 i += 1;
73 }
74 b'>' => {
75 if b.get(i + 1) == Some(&b'=') {
76 out.push((Tok::Ge, col));
77 i += 2;
78 } else {
79 out.push((Tok::Gt, col));
80 i += 1;
81 }
82 }
83 b'<' => {
84 if b.get(i + 1) == Some(&b'=') {
85 out.push((Tok::Le, col));
86 i += 2;
87 } else {
88 out.push((Tok::Lt, col));
89 i += 1;
90 }
91 }
92 b'=' => {
93 if b.get(i + 1) == Some(&b'=') {
94 out.push((Tok::EqEq, col));
95 i += 2;
96 } else {
97 return Err(BpfError {
98 message: "bare '=' — did you mean '=='?".into(),
99 col,
100 });
101 }
102 }
103 b'!' => {
104 if b.get(i + 1) == Some(&b'=') {
105 out.push((Tok::Ne, col));
106 i += 2;
107 } else {
108 return Err(BpfError {
109 message: "bare '!' — did you mean '!='?".into(),
110 col,
111 });
112 }
113 }
114 _ => {
115 let start = i;
116 while i < b.len()
117 && (b[i].is_ascii_alphanumeric()
118 || matches!(b[i], b'.' | b':' | b'/' | b'-' | b'_'))
119 {
120 i += 1;
121 }
122 if i == start {
123 return Err(BpfError {
124 message: format!("unexpected character '{}'", b[col] as char),
125 col,
126 });
127 }
128 out.push((Tok::Word(input[start..i].to_owned()), start));
129 }
130 }
131 }
132 Ok(out)
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139pub enum Dir {
140 Src,
142 Dst,
144 Either,
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub enum CmpOp {
152 Gt,
153 Lt,
154 Ge,
155 Le,
156 Eq,
157 Ne,
158}
159
160#[derive(Debug, Clone)]
164pub enum BpfExpr {
165 And(Box<Self>, Box<Self>),
167 Or(Box<Self>, Box<Self>),
169 Not(Box<Self>),
171 IpProto(u8),
173 Ip,
175 Ip6,
177 Arp,
180 Host { dir: Dir, net: IpNet },
182 Net { dir: Dir, net: IpNet },
184 Port { dir: Dir, range: PortRange },
186 Len { op: CmpOp, val: u32 },
188}
189
190struct Parser {
193 toks: Vec<(Tok, usize)>,
194 pos: usize,
195}
196
197impl Parser {
198 fn peek(&self) -> Option<&Tok> {
199 self.toks.get(self.pos).map(|(t, _)| t)
200 }
201
202 fn peek_word(&self) -> Option<&str> {
203 match self.peek() {
204 Some(Tok::Word(w)) => Some(w.as_str()),
205 _ => None,
206 }
207 }
208
209 fn word_at(&self, offset: usize) -> Option<&str> {
210 match self.toks.get(self.pos + offset) {
211 Some((Tok::Word(w), _)) => Some(w.as_str()),
212 _ => None,
213 }
214 }
215
216 fn col(&self) -> usize {
217 self.toks.get(self.pos).map(|(_, c)| *c).unwrap_or(0)
218 }
219
220 fn advance(&mut self) {
221 self.pos += 1;
222 }
223
224 fn err(&self, msg: impl Into<String>) -> BpfError {
225 BpfError {
226 message: msg.into(),
227 col: self.col(),
228 }
229 }
230
231 fn expect_word(&mut self) -> Result<String, BpfError> {
232 match self.toks.get(self.pos) {
233 Some((Tok::Word(w), _)) => {
234 let w = w.clone();
235 self.pos += 1;
236 Ok(w)
237 }
238 _ => Err(self.err("expected identifier or value")),
239 }
240 }
241
242 fn parse_expr(&mut self) -> Result<BpfExpr, BpfError> {
245 let expr = self.parse_or()?;
246 if self.pos < self.toks.len() {
247 return Err(self.err(format!(
248 "unexpected token after expression: {:?}",
249 self.peek_word().unwrap_or("?")
250 )));
251 }
252 Ok(expr)
253 }
254
255 fn parse_or(&mut self) -> Result<BpfExpr, BpfError> {
256 let mut left = self.parse_and()?;
257 while self.peek_word() == Some("or") {
258 self.advance();
259 let right = self.parse_and()?;
260 left = BpfExpr::Or(Box::new(left), Box::new(right));
261 }
262 Ok(left)
263 }
264
265 fn parse_and(&mut self) -> Result<BpfExpr, BpfError> {
266 let mut left = self.parse_not()?;
267 while self.peek_word() == Some("and") {
268 self.advance();
269 let right = self.parse_not()?;
270 left = BpfExpr::And(Box::new(left), Box::new(right));
271 }
272 Ok(left)
273 }
274
275 fn parse_not(&mut self) -> Result<BpfExpr, BpfError> {
276 if self.peek_word() == Some("not") {
277 self.advance();
278 let inner = self.parse_not()?; return Ok(BpfExpr::Not(Box::new(inner)));
280 }
281 if self.peek() == Some(&Tok::LParen) {
282 self.advance();
283 let inner = self.parse_or()?;
284 if self.peek() != Some(&Tok::RParen) {
285 return Err(self.err("expected ')'"));
286 }
287 self.advance();
288 return Ok(inner);
289 }
290 self.parse_primitive()
291 }
292
293 fn parse_primitive(&mut self) -> Result<BpfExpr, BpfError> {
294 let dir = self.parse_dir();
297
298 let col = self.col();
299 let word = match self.peek_word() {
300 Some(w) => w.to_owned(),
301 None => {
302 if dir != Dir::Either {
303 return Err(
304 self.err("expected 'host', 'net', 'port', or 'portrange' after direction")
305 );
306 }
307 return Err(self.err("expected primitive"));
308 }
309 };
310
311 if dir != Dir::Either {
313 return self.parse_type_prim(dir);
314 }
315
316 match word.as_str() {
317 "tcp" => {
318 self.advance();
319 self.maybe_compound(BpfExpr::IpProto(6))
320 }
321 "udp" => {
322 self.advance();
323 self.maybe_compound(BpfExpr::IpProto(17))
324 }
325 "icmp" => {
326 self.advance();
327 self.maybe_compound(BpfExpr::IpProto(1))
328 }
329 "icmp6" | "icmpv6" => {
330 self.advance();
331 self.maybe_compound(BpfExpr::IpProto(58))
332 }
333 "ip" => {
334 self.advance();
335 self.maybe_compound(BpfExpr::Ip)
336 }
337 "ip6" => {
338 self.advance();
339 self.maybe_compound(BpfExpr::Ip6)
340 }
341 "arp" => {
342 self.advance();
343 Ok(BpfExpr::Arp)
344 }
345 "proto" => {
346 self.advance();
347 let s = self.expect_word()?;
348 let n: u8 = s.parse().map_err(|_| BpfError {
349 message: format!("expected protocol number 0-255, got '{s}'"),
350 col,
351 })?;
352 Ok(BpfExpr::IpProto(n))
353 }
354 "len" => {
355 self.advance();
356 let op = self.parse_cmp_op()?;
357 let s = self.expect_word()?;
358 let n: u32 = s
359 .parse()
360 .map_err(|_| self.err(format!("expected number after 'len', got '{s}'")))?;
361 Ok(BpfExpr::Len { op, val: n })
362 }
363 "host" | "net" | "port" | "portrange" => self.parse_type_prim(Dir::Either),
364 _ => Err(BpfError {
365 message: format!("unknown primitive '{word}'"),
366 col,
367 }),
368 }
369 }
370
371 fn maybe_compound(&mut self, proto_expr: BpfExpr) -> Result<BpfExpr, BpfError> {
375 if self.is_type_keyword() {
376 let type_expr = self.parse_type_prim(Dir::Either)?;
377 Ok(BpfExpr::And(Box::new(proto_expr), Box::new(type_expr)))
378 } else {
379 Ok(proto_expr)
380 }
381 }
382
383 fn is_type_keyword(&self) -> bool {
384 matches!(
385 self.peek_word(),
386 Some("host" | "net" | "port" | "portrange")
387 )
388 }
389
390 fn parse_dir(&mut self) -> Dir {
398 let word = match self.peek_word() {
399 Some(w @ ("src" | "dst")) => w.to_owned(),
400 _ => return Dir::Either,
401 };
402
403 if matches!(self.word_at(1), Some("or" | "and"))
405 && matches!(self.word_at(2), Some("src" | "dst"))
406 && matches!(self.word_at(3), Some("host" | "net" | "port" | "portrange"))
407 {
408 self.pos += 3; return Dir::Either;
410 }
411
412 if matches!(self.word_at(1), Some("host" | "net" | "port" | "portrange")) {
414 self.advance();
415 return if word == "src" { Dir::Src } else { Dir::Dst };
416 }
417
418 Dir::Either }
420
421 fn parse_type_prim(&mut self, dir: Dir) -> Result<BpfExpr, BpfError> {
422 let kw = self.expect_word()?;
423 match kw.as_str() {
424 "host" => {
425 let s = self.expect_word()?;
426 let net = IpNet::parse(&s)
427 .map_err(|_| self.err(format!("invalid host address '{s}'")))?;
428 Ok(BpfExpr::Host { dir, net })
429 }
430 "net" => {
431 let s = self.expect_word()?;
432 let net = IpNet::parse(&s)
433 .map_err(|_| self.err(format!("invalid network/CIDR '{s}'")))?;
434 Ok(BpfExpr::Net { dir, net })
435 }
436 "port" | "portrange" => {
437 let s = self.expect_word()?;
438 let range = PortRange::parse(&s)
439 .map_err(|_| self.err(format!("invalid port or range '{s}'")))?;
440 Ok(BpfExpr::Port { dir, range })
441 }
442 other => Err(self.err(format!("expected host/net/port/portrange, got '{other}'"))),
443 }
444 }
445
446 fn parse_cmp_op(&mut self) -> Result<CmpOp, BpfError> {
447 let col = self.col();
448 let op = match self.peek() {
449 Some(Tok::Gt) => CmpOp::Gt,
450 Some(Tok::Lt) => CmpOp::Lt,
451 Some(Tok::Ge) => CmpOp::Ge,
452 Some(Tok::Le) => CmpOp::Le,
453 Some(Tok::EqEq) => CmpOp::Eq,
454 Some(Tok::Ne) => CmpOp::Ne,
455 _ => {
456 return Err(BpfError {
457 message: "expected comparison operator (>, <, >=, <=, ==, !=)".into(),
458 col,
459 });
460 }
461 };
462 self.advance();
463 Ok(op)
464 }
465}
466
467pub fn parse(input: &str) -> Result<BpfExpr, BpfError> {
480 let toks = tokenize(input)?;
481 if toks.is_empty() {
482 return Err(BpfError {
483 message: "empty filter expression".into(),
484 col: 0,
485 });
486 }
487 let mut parser = Parser { toks, pos: 0 };
488 parser.parse_expr()
489}
490
491impl BpfExpr {
494 pub fn eval(&self, meta: &PacketMeta) -> bool {
498 match self {
499 Self::And(a, b) => a.eval(meta) && b.eval(meta),
500 Self::Or(a, b) => a.eval(meta) || b.eval(meta),
501 Self::Not(inner) => !inner.eval(meta),
502
503 Self::IpProto(proto) => meta
504 .flow_key
505 .as_ref()
506 .map(|k| k.protocol == *proto)
507 .unwrap_or(false),
508
509 Self::Ip => meta
510 .flow_key
511 .as_ref()
512 .map(|k| k.src_ip.is_ipv4())
513 .unwrap_or(false),
514
515 Self::Ip6 => meta
516 .flow_key
517 .as_ref()
518 .map(|k| k.src_ip.is_ipv6())
519 .unwrap_or(false),
520
521 Self::Arp => meta.flow_key.is_none(),
523
524 Self::Host { dir, net } | Self::Net { dir, net } => {
525 let Some(k) = &meta.flow_key else {
526 return false;
527 };
528 match dir {
529 Dir::Src => net.contains(k.src_ip),
530 Dir::Dst => net.contains(k.dst_ip),
531 Dir::Either => net.contains(k.src_ip) || net.contains(k.dst_ip),
532 }
533 }
534
535 Self::Port { dir, range } => {
536 let Some(k) = &meta.flow_key else {
537 return false;
538 };
539 if !matches!(k.protocol, 6 | 17) {
541 return true;
542 }
543 match dir {
544 Dir::Src => range.contains(k.src_port),
545 Dir::Dst => range.contains(k.dst_port),
546 Dir::Either => range.contains(k.src_port) || range.contains(k.dst_port),
547 }
548 }
549
550 Self::Len { op, val } => {
551 let l = meta.captured_len;
552 match op {
553 CmpOp::Gt => l > *val,
554 CmpOp::Lt => l < *val,
555 CmpOp::Ge => l >= *val,
556 CmpOp::Le => l <= *val,
557 CmpOp::Eq => l == *val,
558 CmpOp::Ne => l != *val,
559 }
560 }
561 }
562 }
563}
564
565#[cfg(test)]
568mod tests {
569 use super::*;
570 use std::net::{IpAddr, Ipv4Addr};
571
572 fn v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
573 IpAddr::V4(Ipv4Addr::new(a, b, c, d))
574 }
575
576 fn make_meta(
577 caplen: u32,
578 src: IpAddr,
579 dst: IpAddr,
580 sport: u16,
581 dport: u16,
582 proto: u8,
583 ) -> PacketMeta {
584 use crate::flow::FlowKey;
585 PacketMeta {
586 timestamp_ns: 0,
587 captured_len: caplen,
588 flow_key: Some(FlowKey::new(src, dst, sport, dport, proto)),
589 tcp_flags: 0,
590 }
591 }
592
593 fn no_ip(caplen: u32) -> PacketMeta {
594 PacketMeta {
595 timestamp_ns: 0,
596 captured_len: caplen,
597 flow_key: None,
598 tcp_flags: 0,
599 }
600 }
601
602 #[test]
605 fn test_parse_proto_keywords() {
606 assert!(matches!(parse("tcp").unwrap(), BpfExpr::IpProto(6)));
607 assert!(matches!(parse("udp").unwrap(), BpfExpr::IpProto(17)));
608 assert!(matches!(parse("icmp").unwrap(), BpfExpr::IpProto(1)));
609 assert!(matches!(parse("icmp6").unwrap(), BpfExpr::IpProto(58)));
610 assert!(matches!(parse("icmpv6").unwrap(), BpfExpr::IpProto(58)));
611 assert!(matches!(parse("ip").unwrap(), BpfExpr::Ip));
612 assert!(matches!(parse("ip6").unwrap(), BpfExpr::Ip6));
613 assert!(matches!(parse("arp").unwrap(), BpfExpr::Arp));
614 }
615
616 #[test]
617 fn test_parse_proto_number() {
618 assert!(matches!(parse("proto 17").unwrap(), BpfExpr::IpProto(17)));
619 assert!(matches!(parse("proto 50").unwrap(), BpfExpr::IpProto(50)));
620 }
621
622 #[test]
623 fn test_parse_host_either() {
624 assert!(matches!(
625 parse("host 10.0.0.1").unwrap(),
626 BpfExpr::Host {
627 dir: Dir::Either,
628 ..
629 }
630 ));
631 }
632
633 #[test]
634 fn test_parse_src_dst_host() {
635 assert!(matches!(
636 parse("src host 10.0.0.1").unwrap(),
637 BpfExpr::Host { dir: Dir::Src, .. }
638 ));
639 assert!(matches!(
640 parse("dst host 10.0.0.1").unwrap(),
641 BpfExpr::Host { dir: Dir::Dst, .. }
642 ));
643 }
644
645 #[test]
646 fn test_parse_net_cidr() {
647 assert!(matches!(
648 parse("src net 10.0.0.0/8").unwrap(),
649 BpfExpr::Net { dir: Dir::Src, .. }
650 ));
651 }
652
653 #[test]
654 fn test_parse_port_and_portrange() {
655 assert!(matches!(
656 parse("dst port 443").unwrap(),
657 BpfExpr::Port { dir: Dir::Dst, .. }
658 ));
659 assert!(matches!(
660 parse("portrange 1024-65535").unwrap(),
661 BpfExpr::Port {
662 dir: Dir::Either,
663 ..
664 }
665 ));
666 }
667
668 #[test]
669 fn test_parse_src_or_dst_direction() {
670 let e = parse("src or dst port 80").unwrap();
671 assert!(matches!(
672 e,
673 BpfExpr::Port {
674 dir: Dir::Either,
675 ..
676 }
677 ));
678 }
679
680 #[test]
681 fn test_parse_src_and_dst_direction() {
682 let e = parse("src and dst port 80").unwrap();
683 assert!(matches!(
684 e,
685 BpfExpr::Port {
686 dir: Dir::Either,
687 ..
688 }
689 ));
690 }
691
692 #[test]
693 fn test_parse_len_all_ops() {
694 assert!(matches!(
695 parse("len > 100").unwrap(),
696 BpfExpr::Len {
697 op: CmpOp::Gt,
698 val: 100
699 }
700 ));
701 assert!(matches!(
702 parse("len < 100").unwrap(),
703 BpfExpr::Len {
704 op: CmpOp::Lt,
705 val: 100
706 }
707 ));
708 assert!(matches!(
709 parse("len >= 64").unwrap(),
710 BpfExpr::Len {
711 op: CmpOp::Ge,
712 val: 64
713 }
714 ));
715 assert!(matches!(
716 parse("len <= 1500").unwrap(),
717 BpfExpr::Len {
718 op: CmpOp::Le,
719 val: 1500
720 }
721 ));
722 assert!(matches!(
723 parse("len == 60").unwrap(),
724 BpfExpr::Len {
725 op: CmpOp::Eq,
726 val: 60
727 }
728 ));
729 assert!(matches!(
730 parse("len != 0").unwrap(),
731 BpfExpr::Len {
732 op: CmpOp::Ne,
733 val: 0
734 }
735 ));
736 }
737
738 #[test]
739 fn test_parse_not() {
740 assert!(matches!(parse("not tcp").unwrap(), BpfExpr::Not(_)));
741 }
742
743 #[test]
744 fn test_parse_and_or_precedence() {
745 let e = parse("tcp or udp and dst port 443").unwrap();
747 let BpfExpr::Or(left, right) = e else {
748 panic!("expected Or")
749 };
750 assert!(matches!(*left, BpfExpr::IpProto(6)));
751 assert!(matches!(*right, BpfExpr::And(_, _)));
752 }
753
754 #[test]
755 fn test_parse_tcp_port_sugar() {
756 let BpfExpr::And(l, r) = parse("tcp port 443").unwrap() else {
758 panic!()
759 };
760 assert!(matches!(*l, BpfExpr::IpProto(6)));
761 assert!(matches!(
762 *r,
763 BpfExpr::Port {
764 dir: Dir::Either,
765 ..
766 }
767 ));
768 }
769
770 #[test]
771 fn test_parse_parens() {
772 let BpfExpr::Not(inner) = parse("not (tcp or udp)").unwrap() else {
773 panic!()
774 };
775 assert!(matches!(*inner, BpfExpr::Or(_, _)));
776 }
777
778 #[test]
779 fn test_parse_ipv6_host() {
780 assert!(parse("host 2001:db8::1").is_ok());
781 assert!(parse("dst host ::1").is_ok());
782 }
783
784 #[test]
785 fn test_parse_empty_error() {
786 assert!(parse("").is_err());
787 }
788
789 #[test]
790 fn test_parse_unknown_primitive_error() {
791 assert!(parse("foobar").is_err());
792 }
793
794 #[test]
795 fn test_parse_unclosed_paren_error() {
796 assert!(parse("(tcp and udp").is_err());
797 }
798
799 #[test]
800 fn test_parse_bare_equals_error() {
801 assert!(parse("len = 100").is_err());
802 }
803
804 #[test]
807 fn test_eval_proto_match() {
808 let tcp = make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 1234, 80, 6);
809 let udp = make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 1234, 53, 17);
810 let expr = parse("tcp").unwrap();
811 assert!(expr.eval(&tcp));
812 assert!(!expr.eval(&udp));
813 }
814
815 #[test]
816 fn test_eval_ip_version() {
817 let v4pkt = make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 1, 2, 6);
818 let v6src: IpAddr = "2001:db8::1".parse().unwrap();
819 let v6dst: IpAddr = "2001:db8::2".parse().unwrap();
820 let v6pkt = make_meta(60, v6src, v6dst, 1, 2, 6);
821 assert!(parse("ip").unwrap().eval(&v4pkt));
822 assert!(!parse("ip").unwrap().eval(&v6pkt));
823 assert!(parse("ip6").unwrap().eval(&v6pkt));
824 assert!(!parse("ip6").unwrap().eval(&v4pkt));
825 }
826
827 #[test]
828 fn test_eval_arp_matches_non_ip() {
829 let expr = parse("arp").unwrap();
830 assert!(expr.eval(&no_ip(60)));
831 assert!(!expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 0, 0, 17)));
832 }
833
834 #[test]
835 fn test_eval_host_either() {
836 let expr = parse("host 8.8.8.8").unwrap();
837 assert!(expr.eval(&make_meta(60, v4(8, 8, 8, 8), v4(1, 2, 3, 4), 0, 0, 17)));
838 assert!(expr.eval(&make_meta(60, v4(1, 2, 3, 4), v4(8, 8, 8, 8), 0, 0, 17)));
839 assert!(!expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 0, 0, 17)));
840 }
841
842 #[test]
843 fn test_eval_src_net() {
844 let expr = parse("src net 10.0.0.0/8").unwrap();
845 assert!(expr.eval(&make_meta(60, v4(10, 1, 2, 3), v4(8, 8, 8, 8), 0, 0, 17)));
846 assert!(!expr.eval(&make_meta(60, v4(192, 168, 1, 1), v4(8, 8, 8, 8), 0, 0, 17)));
847 }
848
849 #[test]
850 fn test_eval_dst_port() {
851 let expr = parse("dst port 443").unwrap();
852 assert!(expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 5000, 443, 6)));
853 assert!(!expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 5001, 80, 6)));
854 }
855
856 #[test]
857 fn test_eval_portrange() {
858 let expr = parse("portrange 1024-65535").unwrap();
859 assert!(expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 8080, 80, 6)));
861 assert!(!expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 80, 80, 6)));
863 }
864
865 #[test]
866 fn test_eval_len() {
867 assert!(parse("len > 100").unwrap().eval(&no_ip(200)));
868 assert!(!parse("len > 100").unwrap().eval(&no_ip(50)));
869 assert!(parse("len <= 40").unwrap().eval(&no_ip(40)));
870 assert!(!parse("len <= 40").unwrap().eval(&no_ip(41)));
871 }
872
873 #[test]
874 fn test_eval_tcp_port_sugar() {
875 let expr = parse("tcp port 80").unwrap();
876 assert!(expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 5000, 80, 6)));
877 assert!(!expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 5000, 80, 17))); assert!(!expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 5000, 443, 6))); }
880
881 #[test]
882 fn test_eval_not() {
883 let expr = parse("not tcp").unwrap();
884 assert!(!expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 0, 0, 6)));
885 assert!(expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 0, 0, 17)));
886 }
887
888 #[test]
889 fn test_eval_and_or() {
890 let expr = parse("tcp and dst port 443").unwrap();
891 assert!(expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 5000, 443, 6)));
892 assert!(!expr.eval(&make_meta(60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 5000, 80, 6)));
893 assert!(!expr.eval(&make_meta(
894 60,
895 v4(1, 1, 1, 1),
896 v4(2, 2, 2, 2),
897 5000,
898 443,
899 17
900 )));
901 }
902
903 #[test]
904 fn test_eval_complex() {
905 let expr = parse("(tcp or udp) and src net 10.0.0.0/8 and not dst port 80").unwrap();
907 assert!(expr.eval(&make_meta(
909 100,
910 v4(10, 0, 0, 1),
911 v4(8, 8, 8, 8),
912 5000,
913 443,
914 6
915 )));
916 assert!(!expr.eval(&make_meta(
918 100,
919 v4(10, 0, 0, 1),
920 v4(8, 8, 8, 8),
921 5000,
922 80,
923 6
924 )));
925 assert!(!expr.eval(&make_meta(
927 100,
928 v4(192, 168, 1, 1),
929 v4(8, 8, 8, 8),
930 5000,
931 443,
932 6
933 )));
934 assert!(!expr.eval(&make_meta(100, v4(10, 0, 0, 1), v4(8, 8, 8, 8), 0, 0, 1)));
936 }
937}