1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
14
15use etherparse::{NetSlice, SlicedPacket, TransportSlice};
16use thiserror::Error;
17
18use crate::flow::{FlowKey, normalize_ip};
19
20#[derive(Debug, Error)]
24pub enum FilterError {
25 #[error("invalid IP address or CIDR: '{0}'")]
26 InvalidIp(String),
27
28 #[error("invalid port or range (expected e.g. '443' or '1024-65535'): '{0}'")]
29 InvalidPort(String),
30
31 #[error("invalid protocol (expected 'tcp', 'udp', 'icmp', 'icmp6', or a number 0-255): '{0}'")]
32 InvalidProto(String),
33
34 #[error("invalid flow ID (expected hex, optionally prefixed with '0x'): '{0}'")]
35 InvalidFlowId(String),
36
37 #[error("invalid datetime (expected RFC 3339 or millisecond epoch integer): '{0}'")]
38 InvalidDateTime(String),
39
40 #[error(
41 "invalid TCP flags (e.g. 'SYN', 'SYN+ACK', 'FIN:exact'); \
42 known flags: FIN SYN RST PSH ACK URG ECE CWR: '{0}'"
43 )]
44 InvalidTcpFlags(String),
45
46 #[error("invalid filter operator (expected 'and', 'or', or 'not'): '{0}'")]
47 InvalidOp(String),
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum IpNet {
58 V4 { addr: Ipv4Addr, prefix_len: u8 },
59 V6 { addr: Ipv6Addr, prefix_len: u8 },
60}
61
62impl IpNet {
63 pub fn parse(s: &str) -> Result<Self, FilterError> {
68 let err = || FilterError::InvalidIp(s.to_owned());
69
70 if let Some((ip_str, prefix_str)) = s.split_once('/') {
71 let prefix_len: u8 = prefix_str.parse().map_err(|_| err())?;
72 let ip = normalize_ip(ip_str.parse::<IpAddr>().map_err(|_| err())?);
73 match ip {
74 IpAddr::V4(v4) => {
75 if prefix_len > 32 {
76 return Err(err());
77 }
78 Ok(Self::V4 {
79 addr: v4,
80 prefix_len,
81 })
82 }
83 IpAddr::V6(v6) => {
84 if prefix_len > 128 {
85 return Err(err());
86 }
87 Ok(Self::V6 {
88 addr: v6,
89 prefix_len,
90 })
91 }
92 }
93 } else {
94 match normalize_ip(s.parse::<IpAddr>().map_err(|_| err())?) {
95 IpAddr::V4(v4) => Ok(Self::V4 {
96 addr: v4,
97 prefix_len: 32,
98 }),
99 IpAddr::V6(v6) => Ok(Self::V6 {
100 addr: v6,
101 prefix_len: 128,
102 }),
103 }
104 }
105 }
106
107 pub fn contains(&self, ip: IpAddr) -> bool {
113 let ip = normalize_ip(ip);
114 match (self, ip) {
115 (Self::V4 { addr, prefix_len }, IpAddr::V4(v4)) => {
116 if *prefix_len == 0 {
117 return true;
118 }
119 let shift = 32 - u32::from(*prefix_len);
120 (u32::from(*addr) >> shift) == (u32::from(v4) >> shift)
121 }
122 (Self::V6 { addr, prefix_len }, IpAddr::V6(v6)) => {
123 if *prefix_len == 0 {
124 return true;
125 }
126 let shift = 128 - u128::from(*prefix_len);
127 (u128::from(*addr) >> shift) == (u128::from(v6) >> shift)
128 }
129 _ => false,
130 }
131 }
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub struct PortRange {
139 pub start: u16,
140 pub end: u16,
141}
142
143impl PortRange {
144 pub fn parse(s: &str) -> Result<Self, FilterError> {
150 let err = || FilterError::InvalidPort(s.to_owned());
151 if let Some((lo, hi)) = s.split_once('-') {
152 let start: u16 = lo.trim().parse().map_err(|_| err())?;
153 let end: u16 = hi.trim().parse().map_err(|_| err())?;
154 if start > end {
155 return Err(err());
156 }
157 Ok(Self { start, end })
158 } else {
159 let p: u16 = s.trim().parse().map_err(|_| err())?;
160 Ok(Self { start: p, end: p })
161 }
162 }
163
164 pub fn contains(self, port: u16) -> bool {
166 port >= self.start && port <= self.end
167 }
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub struct TcpFlagsFilter {
183 pub mask: u8,
185 pub value: u8,
187 pub exact: bool,
189}
190
191impl TcpFlagsFilter {
192 pub fn parse(s: &str) -> Result<Self, FilterError> {
204 let err = || FilterError::InvalidTcpFlags(s.to_owned());
205
206 let (flags_part, exact) = if let Some(f) = s.strip_suffix(":exact") {
207 (f, true)
208 } else if let Some(f) = s.strip_suffix(":any") {
209 (f, false)
210 } else {
211 (s, false)
212 };
213
214 let mut mask = 0u8;
215 for token in flags_part.split('+') {
216 let bit = match token.trim().to_ascii_uppercase().as_str() {
217 "FIN" => 0x01,
218 "SYN" => 0x02,
219 "RST" => 0x04,
220 "PSH" => 0x08,
221 "ACK" => 0x10,
222 "URG" => 0x20,
223 "ECE" => 0x40,
224 "CWR" => 0x80,
225 _ => return Err(err()),
226 };
227 mask |= bit;
228 }
229 if mask == 0 {
230 return Err(err());
231 }
232 Ok(Self {
233 mask,
234 value: mask,
235 exact,
236 })
237 }
238
239 pub fn matches(self, flags: u8) -> bool {
241 if self.exact {
242 flags == self.value
244 } else {
245 (flags & self.mask) != 0
246 }
247 }
248}
249
250pub fn parse_proto_list(s: &str) -> Result<Vec<u8>, FilterError> {
260 s.split(',').map(|p| parse_proto(p.trim())).collect()
261}
262
263fn parse_proto(s: &str) -> Result<u8, FilterError> {
264 match s.to_ascii_lowercase().as_str() {
265 "tcp" => Ok(6),
266 "udp" => Ok(17),
267 "icmp" => Ok(1),
268 "icmp6" | "icmpv6" => Ok(58),
269 "sctp" => Ok(132),
270 "esp" => Ok(50),
271 "ah" => Ok(51),
272 other => other
273 .parse::<u8>()
274 .map_err(|_| FilterError::InvalidProto(s.to_owned())),
275 }
276}
277
278pub fn parse_flow_ids(s: &str) -> Result<Vec<u64>, FilterError> {
285 s.split(',')
286 .map(|id| {
287 let id = id.trim().trim_start_matches("0x");
288 u64::from_str_radix(id, 16).map_err(|_| FilterError::InvalidFlowId(id.to_owned()))
289 })
290 .collect()
291}
292
293pub fn parse_datetime_ns(s: &str) -> Result<u64, FilterError> {
300 if let Ok(ms) = s.parse::<u64>() {
302 return Ok(ms.saturating_mul(1_000_000));
303 }
304 chrono::DateTime::parse_from_rfc3339(s)
305 .map_err(|_| FilterError::InvalidDateTime(s.to_owned()))
306 .and_then(|dt| {
307 let ns = dt
308 .timestamp_nanos_opt()
309 .ok_or_else(|| FilterError::InvalidDateTime(s.to_owned()))?;
310 Ok(ns.max(0) as u64)
311 })
312}
313
314#[derive(Debug, Clone)]
321pub struct PacketMeta {
322 pub timestamp_ns: u64,
324 pub captured_len: u32,
326 pub flow_key: Option<FlowKey>,
328 pub tcp_flags: u8,
330}
331
332impl PacketMeta {
333 pub fn from_packet(timestamp_ns: u64, captured_len: u32, data: &[u8]) -> Self {
335 let (flow_key, tcp_flags) = parse_ethernet(data);
336 Self {
337 timestamp_ns,
338 captured_len,
339 flow_key,
340 tcp_flags,
341 }
342 }
343}
344
345fn parse_ethernet(data: &[u8]) -> (Option<FlowKey>, u8) {
346 let sliced = match SlicedPacket::from_ethernet(data) {
347 Ok(s) => s,
348 Err(_) => return (None, 0),
349 };
350
351 let (src_ip, dst_ip, protocol) = match &sliced.net {
352 Some(NetSlice::Ipv4(v4)) => {
353 let h = v4.header();
354 (
355 IpAddr::V4(h.source_addr()),
356 IpAddr::V4(h.destination_addr()),
357 h.protocol().0,
358 )
359 }
360 Some(NetSlice::Ipv6(v6)) => {
361 let h = v6.header();
362 (
363 IpAddr::V6(h.source_addr()),
364 IpAddr::V6(h.destination_addr()),
365 h.next_header().0,
366 )
367 }
368 _ => return (None, 0),
369 };
370
371 let (src_port, dst_port, tcp_flags) = match &sliced.transport {
372 Some(TransportSlice::Tcp(tcp)) => {
373 let mut f = 0u8;
374 if tcp.fin() {
375 f |= 0x01;
376 }
377 if tcp.syn() {
378 f |= 0x02;
379 }
380 if tcp.rst() {
381 f |= 0x04;
382 }
383 if tcp.psh() {
384 f |= 0x08;
385 }
386 if tcp.ack() {
387 f |= 0x10;
388 }
389 if tcp.urg() {
390 f |= 0x20;
391 }
392 if tcp.ece() {
393 f |= 0x40;
394 }
395 if tcp.cwr() {
396 f |= 0x80;
397 }
398 (tcp.source_port(), tcp.destination_port(), f)
399 }
400 Some(TransportSlice::Udp(udp)) => (udp.source_port(), udp.destination_port(), 0),
401 _ => (0, 0, 0),
402 };
403
404 let key = FlowKey::new(src_ip, dst_ip, src_port, dst_port, protocol);
405 (Some(key), tcp_flags)
406}
407
408#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
412pub enum Op {
413 #[default]
415 And,
416 Or,
418 Not,
420}
421
422impl Op {
423 pub fn parse(s: &str) -> Result<Self, FilterError> {
428 match s.to_ascii_lowercase().as_str() {
429 "and" => Ok(Self::And),
430 "or" => Ok(Self::Or),
431 "not" => Ok(Self::Not),
432 _ => Err(FilterError::InvalidOp(s.to_owned())),
433 }
434 }
435}
436
437trait FilterBody {
442 fn protocols(&self) -> &[u8];
443 fn src_ips(&self) -> &[IpNet];
444 fn dst_ips(&self) -> &[IpNet];
445 fn ips(&self) -> &[IpNet];
446 fn src_ports(&self) -> &[PortRange];
447 fn dst_ports(&self) -> &[PortRange];
448 fn ports(&self) -> &[PortRange];
449 fn flow_ids(&self) -> &[u64];
450 fn start_ns(&self) -> Option<u64>;
451 fn end_ns(&self) -> Option<u64>;
452 fn tcp_flags(&self) -> Option<TcpFlagsFilter>;
453 fn min_len(&self) -> Option<u32>;
454 fn max_len(&self) -> Option<u32>;
455 fn unidirectional(&self) -> bool;
456}
457
458macro_rules! impl_filter_body {
460 ($T:ty) => {
461 impl FilterBody for $T {
462 fn protocols(&self) -> &[u8] {
463 &self.protocols
464 }
465 fn src_ips(&self) -> &[IpNet] {
466 &self.src_ips
467 }
468 fn dst_ips(&self) -> &[IpNet] {
469 &self.dst_ips
470 }
471 fn ips(&self) -> &[IpNet] {
472 &self.ips
473 }
474 fn src_ports(&self) -> &[PortRange] {
475 &self.src_ports
476 }
477 fn dst_ports(&self) -> &[PortRange] {
478 &self.dst_ports
479 }
480 fn ports(&self) -> &[PortRange] {
481 &self.ports
482 }
483 fn flow_ids(&self) -> &[u64] {
484 &self.flow_ids
485 }
486 fn start_ns(&self) -> Option<u64> {
487 self.from_ns
488 }
489 fn end_ns(&self) -> Option<u64> {
490 self.to_ns
491 }
492 fn tcp_flags(&self) -> Option<TcpFlagsFilter> {
493 self.tcp_flags
494 }
495 fn min_len(&self) -> Option<u32> {
496 self.min_len
497 }
498 fn max_len(&self) -> Option<u32> {
499 self.max_len
500 }
501 fn unidirectional(&self) -> bool {
502 self.unidirectional
503 }
504 }
505 };
506}
507
508fn eval_body(b: &impl FilterBody, meta: &PacketMeta) -> bool {
514 if let Some(from) = b.start_ns()
516 && meta.timestamp_ns < from
517 {
518 return false;
519 }
520 if let Some(to) = b.end_ns()
521 && meta.timestamp_ns > to
522 {
523 return false;
524 }
525
526 if let Some(min) = b.min_len()
528 && meta.captured_len < min
529 {
530 return false;
531 }
532 if let Some(max) = b.max_len()
533 && meta.captured_len > max
534 {
535 return false;
536 }
537
538 let need_flow = !b.protocols().is_empty()
540 || !b.src_ips().is_empty()
541 || !b.dst_ips().is_empty()
542 || !b.ips().is_empty()
543 || !b.src_ports().is_empty()
544 || !b.dst_ports().is_empty()
545 || !b.ports().is_empty()
546 || !b.flow_ids().is_empty()
547 || b.tcp_flags().is_some();
548
549 if !need_flow {
550 return true;
551 }
552
553 let key = match &meta.flow_key {
555 Some(k) => k,
556 None => return false,
557 };
558
559 if !b.protocols().is_empty() && !b.protocols().contains(&key.protocol) {
561 return false;
562 }
563
564 if !b.src_ips().is_empty() && !b.src_ips().iter().any(|n| n.contains(key.src_ip)) {
566 return false;
567 }
568 if !b.dst_ips().is_empty() && !b.dst_ips().iter().any(|n| n.contains(key.dst_ip)) {
569 return false;
570 }
571 if !b.ips().is_empty()
572 && !b
573 .ips()
574 .iter()
575 .any(|n| n.contains(key.src_ip) || n.contains(key.dst_ip))
576 {
577 return false;
578 }
579
580 if matches!(key.protocol, 6 | 17) {
582 if !b.src_ports().is_empty() && !b.src_ports().iter().any(|r| r.contains(key.src_port)) {
583 return false;
584 }
585 if !b.dst_ports().is_empty() && !b.dst_ports().iter().any(|r| r.contains(key.dst_port)) {
586 return false;
587 }
588 if !b.ports().is_empty()
589 && !b
590 .ports()
591 .iter()
592 .any(|r| r.contains(key.src_port) || r.contains(key.dst_port))
593 {
594 return false;
595 }
596 }
597
598 if !b.flow_ids().is_empty() {
600 let id = key.flow_id(b.unidirectional());
601 if !b.flow_ids().contains(&id) {
602 return false;
603 }
604 }
605
606 if let Some(ff) = b.tcp_flags()
608 && key.protocol == 6
609 && !ff.matches(meta.tcp_flags)
610 {
611 return false;
612 }
613
614 true
615}
616
617#[derive(Debug, Default, Clone)]
644pub struct Filter {
645 pub negate: bool,
647
648 pub rules: Vec<FilterRule>,
650
651 pub protocols: Vec<u8>,
653
654 pub src_ips: Vec<IpNet>,
656 pub dst_ips: Vec<IpNet>,
658 pub ips: Vec<IpNet>,
660
661 pub src_ports: Vec<PortRange>,
663 pub dst_ports: Vec<PortRange>,
665 pub ports: Vec<PortRange>,
667
668 pub flow_ids: Vec<u64>,
670
671 pub from_ns: Option<u64>,
673 pub to_ns: Option<u64>,
675
676 pub tcp_flags: Option<TcpFlagsFilter>,
678
679 pub min_len: Option<u32>,
681 pub max_len: Option<u32>,
683
684 pub unidirectional: bool,
686}
687
688impl_filter_body!(Filter);
689
690impl Filter {
691 pub fn is_empty(&self) -> bool {
695 !self.negate
696 && self.rules.is_empty()
697 && self.protocols.is_empty()
698 && self.src_ips.is_empty()
699 && self.dst_ips.is_empty()
700 && self.ips.is_empty()
701 && self.src_ports.is_empty()
702 && self.dst_ports.is_empty()
703 && self.ports.is_empty()
704 && self.flow_ids.is_empty()
705 && self.from_ns.is_none()
706 && self.to_ns.is_none()
707 && self.tcp_flags.is_none()
708 && self.min_len.is_none()
709 && self.max_len.is_none()
710 }
711
712 pub fn matches(&self, meta: &PacketMeta) -> bool {
720 let base = eval_body(self, meta);
721 let acc = if self.negate { !base } else { base };
722 self.rules.iter().fold(acc, |acc, rule| {
723 let rule_result = eval_body(rule, meta);
724 match rule.op {
725 Op::And => acc && rule_result,
726 Op::Or => acc || rule_result,
727 Op::Not => acc && !rule_result,
728 }
729 })
730 }
731}
732
733#[derive(Debug, Default, Clone)]
741pub struct FilterRule {
742 pub op: Op,
744
745 pub protocols: Vec<u8>,
746 pub src_ips: Vec<IpNet>,
747 pub dst_ips: Vec<IpNet>,
748 pub ips: Vec<IpNet>,
749 pub src_ports: Vec<PortRange>,
750 pub dst_ports: Vec<PortRange>,
751 pub ports: Vec<PortRange>,
752 pub flow_ids: Vec<u64>,
753 pub from_ns: Option<u64>,
754 pub to_ns: Option<u64>,
755 pub tcp_flags: Option<TcpFlagsFilter>,
756 pub min_len: Option<u32>,
757 pub max_len: Option<u32>,
758 pub unidirectional: bool,
759}
760
761impl_filter_body!(FilterRule);
762
763#[cfg(test)]
766mod tests {
767 use super::*;
768 use std::net::Ipv4Addr;
769
770 fn v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
771 IpAddr::V4(Ipv4Addr::new(a, b, c, d))
772 }
773
774 fn meta(
775 ts_ns: u64,
776 caplen: u32,
777 src: IpAddr,
778 dst: IpAddr,
779 sport: u16,
780 dport: u16,
781 proto: u8,
782 tcp_flags: u8,
783 ) -> PacketMeta {
784 PacketMeta {
785 timestamp_ns: ts_ns,
786 captured_len: caplen,
787 flow_key: Some(FlowKey::new(src, dst, sport, dport, proto)),
788 tcp_flags,
789 }
790 }
791
792 fn no_flow_meta(ts_ns: u64, caplen: u32) -> PacketMeta {
793 PacketMeta {
794 timestamp_ns: ts_ns,
795 captured_len: caplen,
796 flow_key: None,
797 tcp_flags: 0,
798 }
799 }
800
801 #[test]
804 fn test_ipnet_parse_host_v4() {
805 let net = IpNet::parse("10.0.0.1").unwrap();
806 assert!(net.contains(v4(10, 0, 0, 1)));
807 assert!(!net.contains(v4(10, 0, 0, 2)));
808 }
809
810 #[test]
811 fn test_ipnet_parse_cidr_v4() {
812 let net = IpNet::parse("10.0.0.0/8").unwrap();
813 assert!(net.contains(v4(10, 0, 0, 1)));
814 assert!(net.contains(v4(10, 255, 255, 255)));
815 assert!(!net.contains(v4(11, 0, 0, 1)));
816 }
817
818 #[test]
819 fn test_ipnet_parse_cidr_v4_slash_32() {
820 let net = IpNet::parse("192.168.1.1/32").unwrap();
821 assert!(net.contains(v4(192, 168, 1, 1)));
822 assert!(!net.contains(v4(192, 168, 1, 2)));
823 }
824
825 #[test]
826 fn test_ipnet_parse_cidr_v6() {
827 let net = IpNet::parse("2001:db8::/32").unwrap();
828 assert!(net.contains("2001:db8::1".parse().unwrap()));
829 assert!(!net.contains("2001:db9::1".parse().unwrap()));
830 }
831
832 #[test]
833 fn test_ipnet_v4_mapped_normalised() {
834 let net = IpNet::parse("10.0.0.0/8").unwrap();
837 let mapped: IpAddr = "::ffff:10.1.2.3".parse().unwrap();
838 assert!(net.contains(mapped));
839 let other_mapped: IpAddr = "::ffff:11.0.0.1".parse().unwrap();
840 assert!(!net.contains(other_mapped));
841 }
842
843 #[test]
844 fn test_ipnet_parse_invalid() {
845 assert!(IpNet::parse("not-an-ip").is_err());
846 assert!(IpNet::parse("10.0.0.0/33").is_err());
847 assert!(IpNet::parse("2001:db8::/129").is_err());
848 }
849
850 #[test]
853 fn test_port_range_single() {
854 let r = PortRange::parse("443").unwrap();
855 assert!(r.contains(443));
856 assert!(!r.contains(444));
857 }
858
859 #[test]
860 fn test_port_range_range() {
861 let r = PortRange::parse("1024-65535").unwrap();
862 assert!(r.contains(1024));
863 assert!(r.contains(65535));
864 assert!(!r.contains(1023));
865 }
866
867 #[test]
868 fn test_port_range_invalid() {
869 assert!(PortRange::parse("abc").is_err());
870 assert!(PortRange::parse("100-50").is_err()); }
872
873 #[test]
876 fn test_tcp_flags_syn_any() {
877 let f = TcpFlagsFilter::parse("SYN").unwrap();
878 assert!(f.matches(0x02)); assert!(f.matches(0x12)); assert!(!f.matches(0x10)); }
882
883 #[test]
884 fn test_tcp_flags_syn_ack_exact() {
885 let f = TcpFlagsFilter::parse("SYN+ACK:exact").unwrap();
886 assert!(f.matches(0x12)); assert!(!f.matches(0x02)); assert!(!f.matches(0x13)); }
890
891 #[test]
892 fn test_tcp_flags_invalid() {
893 assert!(TcpFlagsFilter::parse("").is_err());
894 assert!(TcpFlagsFilter::parse("INVALID").is_err());
895 }
896
897 #[test]
900 fn test_parse_proto_list() {
901 assert_eq!(parse_proto_list("tcp,udp").unwrap(), [6, 17]);
902 assert_eq!(parse_proto_list("icmp").unwrap(), [1]);
903 assert_eq!(parse_proto_list("6,17").unwrap(), [6, 17]);
904 assert!(parse_proto_list("foobar").is_err());
905 }
906
907 #[test]
910 fn test_parse_flow_ids() {
911 assert_eq!(parse_flow_ids("deadbeef").unwrap(), [0xdeadbeefu64]);
912 assert_eq!(
913 parse_flow_ids("0xdeadbeef,0xcafe1234").unwrap(),
914 [0xdeadbeefu64, 0xcafe1234u64]
915 );
916 assert!(parse_flow_ids("xyz").is_err());
917 }
918
919 #[test]
922 fn test_parse_datetime_ms_epoch() {
923 assert_eq!(parse_datetime_ns("1000").unwrap(), 1_000_000_000);
924 }
925
926 #[test]
927 fn test_parse_datetime_rfc3339() {
928 let ns = parse_datetime_ns("1970-01-01T00:00:01Z").unwrap();
929 assert_eq!(ns, 1_000_000_000);
930 }
931
932 #[test]
933 fn test_parse_datetime_invalid() {
934 assert!(parse_datetime_ns("not-a-date").is_err());
935 }
936
937 #[test]
940 fn test_empty_filter_matches_everything() {
941 let f = Filter::default();
942 assert!(f.is_empty());
943 assert!(f.matches(&meta(0, 100, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 80, 443, 6, 0)));
944 assert!(f.matches(&no_flow_meta(0, 100)));
945 }
946
947 #[test]
948 fn test_filter_time_range() {
949 let mut f = Filter::default();
950 f.from_ns = Some(1_000);
951 f.to_ns = Some(2_000);
952 assert!(f.matches(&no_flow_meta(1_000, 10)));
953 assert!(f.matches(&no_flow_meta(2_000, 10)));
954 assert!(!f.matches(&no_flow_meta(999, 10)));
955 assert!(!f.matches(&no_flow_meta(2_001, 10)));
956 }
957
958 #[test]
959 fn test_filter_packet_length() {
960 let mut f = Filter::default();
961 f.min_len = Some(50);
962 f.max_len = Some(100);
963 assert!(f.matches(&no_flow_meta(0, 50)));
964 assert!(f.matches(&no_flow_meta(0, 100)));
965 assert!(!f.matches(&no_flow_meta(0, 49)));
966 assert!(!f.matches(&no_flow_meta(0, 101)));
967 }
968
969 #[test]
970 fn test_filter_protocol_tcp_only() {
971 let mut f = Filter::default();
972 f.protocols = vec![6]; let tcp = meta(0, 100, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 1234, 80, 6, 0);
974 let udp = meta(0, 100, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 1234, 53, 17, 0);
975 assert!(f.matches(&tcp));
976 assert!(!f.matches(&udp));
977 }
978
979 #[test]
980 fn test_filter_protocol_multiple_or() {
981 let mut f = Filter::default();
982 f.protocols = vec![6, 17]; assert!(f.matches(&meta(0, 100, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 1, 2, 6, 0)));
984 assert!(f.matches(&meta(0, 100, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 1, 2, 17, 0)));
985 assert!(!f.matches(&meta(0, 100, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 0, 0, 1, 0))); }
987
988 #[test]
989 fn test_filter_src_ip_cidr() {
990 let mut f = Filter::default();
991 f.src_ips = vec![IpNet::parse("10.0.0.0/8").unwrap()];
992 assert!(f.matches(&meta(0, 60, v4(10, 1, 2, 3), v4(8, 8, 8, 8), 0, 0, 17, 0)));
993 assert!(!f.matches(&meta(0, 60, v4(11, 0, 0, 1), v4(8, 8, 8, 8), 0, 0, 17, 0)));
994 }
995
996 #[test]
997 fn test_filter_dst_ip() {
998 let mut f = Filter::default();
999 f.dst_ips = vec![IpNet::parse("8.8.8.8").unwrap()];
1000 assert!(f.matches(&meta(0, 60, v4(1, 2, 3, 4), v4(8, 8, 8, 8), 0, 0, 17, 0)));
1001 assert!(!f.matches(&meta(0, 60, v4(1, 2, 3, 4), v4(1, 1, 1, 1), 0, 0, 17, 0)));
1002 }
1003
1004 #[test]
1005 fn test_filter_ip_either_endpoint() {
1006 let mut f = Filter::default();
1007 f.ips = vec![IpNet::parse("10.0.0.1").unwrap()];
1008 assert!(f.matches(&meta(0, 60, v4(10, 0, 0, 1), v4(8, 8, 8, 8), 0, 0, 17, 0)));
1010 assert!(f.matches(&meta(0, 60, v4(8, 8, 8, 8), v4(10, 0, 0, 1), 0, 0, 17, 0)));
1011 assert!(!f.matches(&meta(0, 60, v4(1, 2, 3, 4), v4(5, 6, 7, 8), 0, 0, 17, 0)));
1012 }
1013
1014 #[test]
1015 fn test_filter_src_ip_multiple_or() {
1016 let mut f = Filter::default();
1017 f.src_ips = vec![
1018 IpNet::parse("10.0.0.1").unwrap(),
1019 IpNet::parse("192.168.0.0/16").unwrap(),
1020 ];
1021 assert!(f.matches(&meta(0, 60, v4(10, 0, 0, 1), v4(8, 8, 8, 8), 0, 0, 6, 0)));
1022 assert!(f.matches(&meta(0, 60, v4(192, 168, 1, 2), v4(8, 8, 8, 8), 0, 0, 6, 0)));
1023 assert!(!f.matches(&meta(0, 60, v4(172, 16, 0, 1), v4(8, 8, 8, 8), 0, 0, 6, 0)));
1024 }
1025
1026 #[test]
1027 fn test_filter_dst_port() {
1028 let mut f = Filter::default();
1029 f.dst_ports = vec![PortRange {
1030 start: 443,
1031 end: 443,
1032 }];
1033 assert!(f.matches(&meta(
1034 0,
1035 60,
1036 v4(1, 1, 1, 1),
1037 v4(2, 2, 2, 2),
1038 1234,
1039 443,
1040 6,
1041 0
1042 )));
1043 assert!(!f.matches(&meta(0, 60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 1234, 80, 6, 0)));
1044 }
1045
1046 #[test]
1047 fn test_filter_port_range_either() {
1048 let mut f = Filter::default();
1049 f.ports = vec![PortRange {
1050 start: 8000,
1051 end: 9000,
1052 }];
1053 assert!(f.matches(&meta(
1054 0,
1055 60,
1056 v4(1, 1, 1, 1),
1057 v4(2, 2, 2, 2),
1058 8080,
1059 1234,
1060 6,
1061 0
1062 )));
1063 assert!(f.matches(&meta(
1064 0,
1065 60,
1066 v4(1, 1, 1, 1),
1067 v4(2, 2, 2, 2),
1068 1234,
1069 8080,
1070 6,
1071 0
1072 )));
1073 assert!(!f.matches(&meta(0, 60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 80, 443, 6, 0)));
1074 }
1075
1076 #[test]
1077 fn test_filter_port_ignored_for_icmp() {
1078 let mut f = Filter::default();
1080 f.dst_ports = vec![PortRange {
1081 start: 443,
1082 end: 443,
1083 }];
1084 assert!(f.matches(&meta(0, 60, v4(1, 1, 1, 1), v4(2, 2, 2, 2), 0, 0, 1, 0)));
1088 }
1089
1090 #[test]
1091 fn test_filter_flow_id() {
1092 let key = FlowKey::new(v4(10, 0, 0, 1), v4(10, 0, 0, 2), 1234, 443, 6);
1093 let id = key.flow_id(false);
1094 let mut f = Filter::default();
1095 f.flow_ids = vec![id];
1096
1097 let matching = PacketMeta {
1098 timestamp_ns: 0,
1099 captured_len: 60,
1100 flow_key: Some(key),
1101 tcp_flags: 0,
1102 };
1103 assert!(f.matches(&matching));
1104
1105 let other = PacketMeta {
1106 timestamp_ns: 0,
1107 captured_len: 60,
1108 flow_key: Some(FlowKey::new(v4(10, 0, 0, 3), v4(10, 0, 0, 4), 5678, 80, 6)),
1109 tcp_flags: 0,
1110 };
1111 assert!(!f.matches(&other));
1112 }
1113
1114 #[test]
1115 fn test_filter_tcp_flags() {
1116 let mut f = Filter::default();
1117 f.tcp_flags = Some(TcpFlagsFilter::parse("SYN").unwrap());
1118
1119 assert!(f.matches(&meta(
1121 0,
1122 60,
1123 v4(1, 1, 1, 1),
1124 v4(2, 2, 2, 2),
1125 1234,
1126 80,
1127 6,
1128 0x02
1129 )));
1130 assert!(!f.matches(&meta(
1132 0,
1133 60,
1134 v4(1, 1, 1, 1),
1135 v4(2, 2, 2, 2),
1136 1234,
1137 80,
1138 6,
1139 0x10
1140 )));
1141 }
1142
1143 #[test]
1144 fn test_filter_and_composition() {
1145 let mut f = Filter::default();
1147 f.protocols = vec![6];
1148 f.dst_ports = vec![PortRange {
1149 start: 443,
1150 end: 443,
1151 }];
1152 f.src_ips = vec![IpNet::parse("10.0.0.0/8").unwrap()];
1153
1154 assert!(f.matches(&meta(
1156 0,
1157 60,
1158 v4(10, 1, 2, 3),
1159 v4(8, 8, 8, 8),
1160 5000,
1161 443,
1162 6,
1163 0
1164 )));
1165 assert!(!f.matches(&meta(
1167 0,
1168 60,
1169 v4(10, 1, 2, 3),
1170 v4(8, 8, 8, 8),
1171 5000,
1172 443,
1173 17,
1174 0
1175 )));
1176 assert!(!f.matches(&meta(
1178 0,
1179 60,
1180 v4(10, 1, 2, 3),
1181 v4(8, 8, 8, 8),
1182 5000,
1183 80,
1184 6,
1185 0
1186 )));
1187 assert!(!f.matches(&meta(
1189 0,
1190 60,
1191 v4(11, 0, 0, 1),
1192 v4(8, 8, 8, 8),
1193 5000,
1194 443,
1195 6,
1196 0
1197 )));
1198 }
1199
1200 #[test]
1201 fn test_filter_non_ip_fails_when_flow_filter_active() {
1202 let mut f = Filter::default();
1203 f.protocols = vec![6];
1204 assert!(!f.matches(&no_flow_meta(0, 100)));
1205 }
1206
1207 #[test]
1208 fn test_filter_non_ip_passes_when_only_length_filter() {
1209 let mut f = Filter::default();
1210 f.min_len = Some(50);
1211 assert!(f.matches(&no_flow_meta(0, 60)));
1212 assert!(!f.matches(&no_flow_meta(0, 40)));
1213 }
1214}