1use pest::Parser;
6use pest::iterators::Pair;
7
8use fidl_fuchsia_hardware_network as fhnet;
9use fidl_fuchsia_net as net;
10use fidl_fuchsia_net_filter_deprecated as filter;
11
12use crate::grammar::{Error, FilterRuleParser, InvalidReason, Rule};
13use crate::util;
14
15fn parse_action(pair: Pair<'_, Rule>) -> filter::Action {
16 assert_eq!(pair.as_rule(), Rule::action);
17 match pair.into_inner().next().unwrap().as_rule() {
18 Rule::pass => filter::Action::Pass,
19 Rule::drop => filter::Action::Drop,
20 Rule::dropreset => filter::Action::DropReset,
21 _ => unreachable!("action must be one of (pass|drop|dropreset)"),
22 }
23}
24
25fn parse_direction(pair: Pair<'_, Rule>) -> filter::Direction {
26 assert_eq!(pair.as_rule(), Rule::direction);
27 match pair.into_inner().next().unwrap().as_rule() {
28 Rule::incoming => filter::Direction::Incoming,
29 Rule::outgoing => filter::Direction::Outgoing,
30 _ => unreachable!("direction must be one of (in|out)"),
31 }
32}
33
34fn parse_proto(pair: Pair<'_, Rule>) -> filter::SocketProtocol {
35 assert_eq!(pair.as_rule(), Rule::proto);
36 match pair.into_inner().next() {
37 Some(pair) => match pair.as_rule() {
38 Rule::tcp => filter::SocketProtocol::Tcp,
39 Rule::udp => filter::SocketProtocol::Udp,
40 Rule::icmp => filter::SocketProtocol::Icmp,
41 _ => unreachable!("protocol must be one of (tcp|udp|icmp)"),
42 },
43 None => filter::SocketProtocol::Any,
44 }
45}
46
47fn parse_devclass(pair: Pair<'_, Rule>) -> filter::DeviceClass {
48 assert_eq!(pair.as_rule(), Rule::devclass);
49 match pair.into_inner().next() {
50 Some(pair) => match pair.as_rule() {
51 Rule::virt => filter::DeviceClass::Match_(fhnet::PortClass::Virtual),
52 Rule::ethernet => filter::DeviceClass::Match_(fhnet::PortClass::Ethernet),
53 Rule::wlan => filter::DeviceClass::Match_(fhnet::PortClass::WlanClient),
54 Rule::ppp => filter::DeviceClass::Match_(fhnet::PortClass::Ppp),
55 Rule::bridge => filter::DeviceClass::Match_(fhnet::PortClass::Bridge),
56 Rule::ap => filter::DeviceClass::Match_(fhnet::PortClass::WlanAp),
57 Rule::lowpan => filter::DeviceClass::Match_(fhnet::PortClass::Lowpan),
58 _ => unreachable!("devclass must be one of (virt|ethernet|wlan|ppp|bridge|ap|lowpan)"),
59 },
60 None => filter::DeviceClass::Any(filter::Empty {}),
61 }
62}
63
64fn parse_src(
65 pair: Pair<'_, Rule>,
66) -> Result<(Option<Box<net::Subnet>>, bool, filter::PortRange), Error> {
67 assert_eq!(pair.as_rule(), Rule::src);
68 parse_src_or_dst(pair)
69}
70
71fn parse_dst(
72 pair: Pair<'_, Rule>,
73) -> Result<(Option<Box<net::Subnet>>, bool, filter::PortRange), Error> {
74 assert_eq!(pair.as_rule(), Rule::dst);
75 parse_src_or_dst(pair)
76}
77
78fn parse_src_or_dst(
79 pair: Pair<'_, Rule>,
80) -> Result<(Option<Box<net::Subnet>>, bool, filter::PortRange), Error> {
81 let mut inner = pair.into_inner();
82 match inner.next() {
83 Some(pair) => match pair.as_rule() {
84 Rule::invertible_subnet => {
85 let (subnet, invert_match) = util::parse_invertible_subnet(pair)?;
86 let port = match inner.next() {
87 Some(pair) => parse_port_range(pair)?,
88 None => filter::PortRange { start: 0, end: 0 },
89 };
90 Ok((Some(Box::new(subnet)), invert_match, port))
91 }
92 Rule::port_range => Ok((None, false, parse_port_range(pair)?)),
93 _ => unreachable!("src or dst must be either an invertible subnet or port range"),
94 },
95 None => Ok((None, false, filter::PortRange { start: 0, end: 0 })),
96 }
97}
98
99fn parse_port_range(pair: Pair<'_, Rule>) -> Result<filter::PortRange, Error> {
100 assert_eq!(pair.as_rule(), Rule::port_range);
101 let mut inner = pair.into_inner();
102 let pair = inner.next().unwrap();
103 match pair.as_rule() {
104 Rule::port => {
105 let port_num = util::parse_port_num(inner.next().unwrap())?;
106 Ok(filter::PortRange { start: port_num, end: port_num })
107 }
108 Rule::range => {
109 let port_start = util::parse_port_num(inner.next().unwrap())?;
110 let port_end = util::parse_port_num(inner.next().unwrap())?;
111 Ok(filter::PortRange { start: port_start, end: port_end })
112 }
113 _ => unreachable!("port range must be either a single port, or a port range"),
114 }
115}
116
117fn parse_log(pair: Pair<'_, Rule>) -> bool {
118 assert_eq!(pair.as_rule(), Rule::log);
119 pair.as_str() == "log"
120}
121
122fn parse_state(pair: Pair<'_, Rule>) -> bool {
123 assert_eq!(pair.as_rule(), Rule::state);
124 let mut inner = pair.into_inner();
125 match inner.next() {
126 Some(pair) => {
127 assert_eq!(pair.as_rule(), Rule::state_adj);
128 match pair.as_str() {
129 "no" => false,
130 "keep" => true,
131 _ => unreachable!("state must be either (no|keep)"),
132 }
133 }
134 None => false, }
136}
137
138fn parse_rule(pair: Pair<'_, Rule>) -> Result<filter::Rule, Error> {
139 assert_eq!(pair.as_rule(), Rule::rule);
140 let mut pairs = pair.into_inner();
141
142 let action = parse_action(pairs.next().unwrap());
143 let direction = parse_direction(pairs.next().unwrap());
144 let proto = parse_proto(pairs.next().unwrap());
145 let device_class = parse_devclass(pairs.next().unwrap());
146 let (src_subnet, src_subnet_invert_match, src_port_range) = parse_src(pairs.next().unwrap())?;
147 let (dst_subnet, dst_subnet_invert_match, dst_port_range) = parse_dst(pairs.next().unwrap())?;
148 let log = parse_log(pairs.next().unwrap());
149 let keep_state = parse_state(pairs.next().unwrap());
150
151 Ok(filter::Rule {
152 action,
153 direction,
154 proto,
155 src_subnet,
156 src_subnet_invert_match,
157 src_port_range,
158 dst_subnet,
159 dst_subnet_invert_match,
160 dst_port_range,
161 nic: 0, log,
163 keep_state,
164 device_class,
165 })
166}
167
168fn parse_nat(pair: Pair<'_, Rule>) -> Result<filter::Nat, Error> {
169 assert_eq!(pair.as_rule(), Rule::nat);
170 let mut pairs = pair.into_inner();
171
172 let proto = parse_proto(pairs.next().unwrap());
173 let src_subnet = util::parse_subnet(pairs.next().unwrap())?;
174
175 Ok(filter::Nat {
176 proto,
177 src_subnet,
178 outgoing_nic: 0, })
180}
181
182fn parse_rdr(pair: Pair<'_, Rule>) -> Result<filter::Rdr, Error> {
183 assert_eq!(pair.as_rule(), Rule::rdr);
184 let mut pairs = pair.into_inner();
185
186 let proto = parse_proto(pairs.next().unwrap());
187 let dst_addr = util::parse_ipaddr(pairs.next().unwrap())?;
188 let dst_port_range = parse_port_range(pairs.next().unwrap())?;
189 let new_dst_addr = util::parse_ipaddr(pairs.next().unwrap())?;
190 let new_dst_port_range = parse_port_range(pairs.next().unwrap())?;
191
192 Ok(filter::Rdr {
193 proto,
194 dst_addr,
195 dst_port_range,
196 new_dst_addr,
197 new_dst_port_range,
198 nic: 0, })
200}
201
202fn validate_port_range(range: &filter::PortRange) -> Result<(), Error> {
203 if range.start > range.end {
204 return Err(Error::Invalid(InvalidReason::InvalidPortRange));
205 }
206 Ok(())
207}
208
209fn port_range_length(range: &filter::PortRange) -> Result<u16, Error> {
210 validate_port_range(&range)?;
211 Ok(range.end - range.start)
212}
213
214fn validate_rule(rule: &filter::Rule) -> Result<(), Error> {
215 if let (Some(src_subnet), Some(dst_subnet)) = (&rule.src_subnet, &rule.dst_subnet) {
216 if !util::ip_version_eq(&src_subnet.addr, &dst_subnet.addr) {
217 return Err(Error::Invalid(InvalidReason::MixedIPVersions));
218 }
219 }
220 validate_port_range(&rule.src_port_range)?;
221 validate_port_range(&rule.dst_port_range)?;
222
223 Ok(())
224}
225
226fn validate_rdr(rdr: &filter::Rdr) -> Result<(), Error> {
227 if !util::ip_version_eq(&rdr.dst_addr, &rdr.new_dst_addr) {
228 return Err(Error::Invalid(InvalidReason::MixedIPVersions));
229 }
230 if port_range_length(&rdr.dst_port_range)? != port_range_length(&rdr.new_dst_port_range)? {
231 return Err(Error::Invalid(InvalidReason::PortRangeLengthMismatch));
232 }
233
234 Ok(())
235}
236
237pub fn parse_str_to_rules(line: &str) -> Result<Vec<filter::Rule>, Error> {
238 let mut pairs =
239 FilterRuleParser::parse(Rule::rules, &line).map_err(|err| Error::Pest(Box::new(err)))?;
240 let mut rules = Vec::new();
241 for filter_rule in pairs.next().unwrap().into_inner() {
242 match filter_rule.as_rule() {
243 Rule::rule => {
244 let rule = parse_rule(filter_rule)?;
245 validate_rule(&rule)?;
246 rules.push(rule);
247 }
248 Rule::EOI => (),
249 _ => unreachable!("rule must only have a rule case"),
250 }
251 }
252 Ok(rules)
253}
254
255pub fn parse_str_to_nat_rules(line: &str) -> Result<Vec<filter::Nat>, Error> {
256 let mut pairs = FilterRuleParser::parse(Rule::nat_rules, &line)
257 .map_err(|err| Error::Pest(Box::new(err)))?;
258 let mut nat_rules = Vec::new();
259 for filter_rule in pairs.next().unwrap().into_inner() {
260 match filter_rule.as_rule() {
261 Rule::nat => {
262 let nat = parse_nat(filter_rule)?;
263 nat_rules.push(nat);
264 }
265 Rule::EOI => (),
266 _ => unreachable!("nat must only have a nat case"),
267 }
268 }
269 Ok(nat_rules)
270}
271
272pub fn parse_str_to_rdr_rules(line: &str) -> Result<Vec<filter::Rdr>, Error> {
273 let mut pairs = FilterRuleParser::parse(Rule::rdr_rules, &line)
274 .map_err(|err| Error::Pest(Box::new(err)))?;
275 let mut rdr_rules = Vec::new();
276 for filter_rule in pairs.next().unwrap().into_inner() {
277 match filter_rule.as_rule() {
278 Rule::rdr => {
279 let rdr = parse_rdr(filter_rule)?;
280 validate_rdr(&rdr)?;
281 rdr_rules.push(rdr);
282 }
283 Rule::EOI => (),
284 _ => unreachable!("rdr must only have a rdr case"),
285 }
286 }
287 Ok(rdr_rules)
288}
289
290#[cfg(test)]
291mod test {
292 use super::*;
293
294 use net_declare::{fidl_ip, fidl_subnet};
295
296 #[test]
297 fn test_rule_with_proto_any() {
298 assert_eq!(
299 parse_str_to_rules("pass in;"),
300 Ok(vec![filter::Rule {
301 action: filter::Action::Pass,
302 direction: filter::Direction::Incoming,
303 proto: filter::SocketProtocol::Any,
304 src_subnet: None,
305 src_subnet_invert_match: false,
306 src_port_range: filter::PortRange { start: 0, end: 0 },
307 dst_subnet: None,
308 dst_subnet_invert_match: false,
309 dst_port_range: filter::PortRange { start: 0, end: 0 },
310 nic: 0,
311 log: false,
312 keep_state: false,
313 device_class: filter::DeviceClass::Any(filter::Empty {}),
314 }])
315 );
316 }
317
318 #[test]
319 fn test_rule_with_proto_tcp() {
320 assert_eq!(
321 parse_str_to_rules("pass in proto tcp;"),
322 Ok(vec![filter::Rule {
323 action: filter::Action::Pass,
324 direction: filter::Direction::Incoming,
325 proto: filter::SocketProtocol::Tcp,
326 src_subnet: None,
327 src_subnet_invert_match: false,
328 src_port_range: filter::PortRange { start: 0, end: 0 },
329 dst_subnet: None,
330 dst_subnet_invert_match: false,
331 dst_port_range: filter::PortRange { start: 0, end: 0 },
332 nic: 0,
333 log: false,
334 keep_state: false,
335 device_class: filter::DeviceClass::Any(filter::Empty {}),
336 }])
337 );
338 }
339
340 #[test]
341 fn test_multiple_rules() {
342 assert_eq!(
343 parse_str_to_rules("pass in proto tcp; drop out proto udp;"),
344 Ok(vec![
345 filter::Rule {
346 action: filter::Action::Pass,
347 direction: filter::Direction::Incoming,
348 proto: filter::SocketProtocol::Tcp,
349 src_subnet: None,
350 src_subnet_invert_match: false,
351 src_port_range: filter::PortRange { start: 0, end: 0 },
352 dst_subnet: None,
353 dst_subnet_invert_match: false,
354 dst_port_range: filter::PortRange { start: 0, end: 0 },
355 nic: 0,
356 log: false,
357 keep_state: false,
358 device_class: filter::DeviceClass::Any(filter::Empty {}),
359 },
360 filter::Rule {
361 action: filter::Action::Drop,
362 direction: filter::Direction::Outgoing,
363 proto: filter::SocketProtocol::Udp,
364 src_subnet: None,
365 src_subnet_invert_match: false,
366 src_port_range: filter::PortRange { start: 0, end: 0 },
367 dst_subnet: None,
368 dst_subnet_invert_match: false,
369 dst_port_range: filter::PortRange { start: 0, end: 0 },
370 nic: 0,
371 log: false,
372 keep_state: false,
373 device_class: filter::DeviceClass::Any(filter::Empty {}),
374 },
375 ])
376 );
377 }
378
379 #[test]
380 fn test_rule_with_from_v4_address() {
381 assert_eq!(
382 parse_str_to_rules("pass in proto tcp from 1.2.3.4/24;"),
383 Ok(vec![filter::Rule {
384 action: filter::Action::Pass,
385 direction: filter::Direction::Incoming,
386 proto: filter::SocketProtocol::Tcp,
387 src_subnet: Some(Box::new(fidl_subnet!("1.2.3.4/24"))),
388 src_subnet_invert_match: false,
389 src_port_range: filter::PortRange { start: 0, end: 0 },
390 dst_subnet: None,
391 dst_subnet_invert_match: false,
392 dst_port_range: filter::PortRange { start: 0, end: 0 },
393 nic: 0,
394 log: false,
395 keep_state: false,
396 device_class: filter::DeviceClass::Any(filter::Empty {}),
397 }])
398 );
399 }
400
401 #[test]
402 fn test_rule_with_from_port() {
403 assert_eq!(
404 parse_str_to_rules("pass in proto tcp from port 10000;"),
405 Ok(vec![filter::Rule {
406 action: filter::Action::Pass,
407 direction: filter::Direction::Incoming,
408 proto: filter::SocketProtocol::Tcp,
409 src_subnet: None,
410 src_subnet_invert_match: false,
411 src_port_range: filter::PortRange { start: 10000, end: 10000 },
412 dst_subnet: None,
413 dst_subnet_invert_match: false,
414 dst_port_range: filter::PortRange { start: 0, end: 0 },
415 nic: 0,
416 log: false,
417 keep_state: false,
418 device_class: filter::DeviceClass::Any(filter::Empty {}),
419 }])
420 );
421 }
422
423 #[test]
424 fn test_rule_with_from_range() {
425 assert_eq!(
426 parse_str_to_rules("pass in proto tcp from range 10000:10010;"),
427 Ok(vec![filter::Rule {
428 action: filter::Action::Pass,
429 direction: filter::Direction::Incoming,
430 proto: filter::SocketProtocol::Tcp,
431 src_subnet: None,
432 src_subnet_invert_match: false,
433 src_port_range: filter::PortRange { start: 10000, end: 10010 },
434 dst_subnet: None,
435 dst_subnet_invert_match: false,
436 dst_port_range: filter::PortRange { start: 0, end: 0 },
437 nic: 0,
438 log: false,
439 keep_state: false,
440 device_class: filter::DeviceClass::Any(filter::Empty {}),
441 }])
442 );
443 }
444
445 #[test]
446 fn test_rule_with_from_range_2() {
447 assert_eq!(
448 parse_str_to_rules("pass in proto tcp from range 0:5;"),
449 Ok(vec![filter::Rule {
450 action: filter::Action::Pass,
451 direction: filter::Direction::Incoming,
452 proto: filter::SocketProtocol::Tcp,
453 src_subnet: None,
454 src_subnet_invert_match: false,
455 src_port_range: filter::PortRange { start: 0, end: 5 },
456 dst_subnet: None,
457 dst_subnet_invert_match: false,
458 dst_port_range: filter::PortRange { start: 0, end: 0 },
459 nic: 0,
460 log: false,
461 keep_state: false,
462 device_class: filter::DeviceClass::Any(filter::Empty {}),
463 }])
464 );
465 }
466
467 #[test]
468 fn test_rule_with_from_invalid_range() {
469 assert_eq!(
470 parse_str_to_rules("pass in proto tcp from range 10005:10000;"),
471 Err(Error::Invalid(InvalidReason::InvalidPortRange))
472 );
473 }
474
475 #[test]
476 fn test_rule_with_from_v4_address_port() {
477 assert_eq!(
478 parse_str_to_rules("pass in proto tcp from 1.2.3.4/24 port 10000;"),
479 Ok(vec![filter::Rule {
480 action: filter::Action::Pass,
481 direction: filter::Direction::Incoming,
482 proto: filter::SocketProtocol::Tcp,
483 src_subnet: Some(Box::new(fidl_subnet!("1.2.3.4/24"))),
484 src_subnet_invert_match: false,
485 src_port_range: filter::PortRange { start: 10000, end: 10000 },
486 dst_subnet: None,
487 dst_subnet_invert_match: false,
488 dst_port_range: filter::PortRange { start: 0, end: 0 },
489 nic: 0,
490 log: false,
491 keep_state: false,
492 device_class: filter::DeviceClass::Any(filter::Empty {}),
493 }])
494 );
495 }
496
497 #[test]
498 fn test_rule_with_from_not_v4_address_port() {
499 assert_eq!(
500 parse_str_to_rules("pass in proto tcp from !1.2.3.4/24 port 10000;"),
501 Ok(vec![filter::Rule {
502 action: filter::Action::Pass,
503 direction: filter::Direction::Incoming,
504 proto: filter::SocketProtocol::Tcp,
505 src_subnet: Some(Box::new(fidl_subnet!("1.2.3.4/24"))),
506 src_subnet_invert_match: true,
507 src_port_range: filter::PortRange { start: 10000, end: 10000 },
508 dst_subnet: None,
509 dst_subnet_invert_match: false,
510 dst_port_range: filter::PortRange { start: 0, end: 0 },
511 nic: 0,
512 log: false,
513 keep_state: false,
514 device_class: filter::DeviceClass::Any(filter::Empty {}),
515 }])
516 );
517 }
518
519 #[test]
520 fn test_rule_with_from_v6_address_port() {
521 assert_eq!(
522 parse_str_to_rules("pass in proto tcp from 1234:5678::/32 port 10000;"),
523 Ok(vec![filter::Rule {
524 action: filter::Action::Pass,
525 direction: filter::Direction::Incoming,
526 proto: filter::SocketProtocol::Tcp,
527 src_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
528 src_subnet_invert_match: false,
529 src_port_range: filter::PortRange { start: 10000, end: 10000 },
530 dst_subnet: None,
531 dst_subnet_invert_match: false,
532 dst_port_range: filter::PortRange { start: 0, end: 0 },
533 nic: 0,
534 log: false,
535 keep_state: false,
536 device_class: filter::DeviceClass::Any(filter::Empty {}),
537 }])
538 );
539 }
540
541 #[test]
542 fn test_rule_with_to_v6_address_port() {
543 assert_eq!(
544 parse_str_to_rules("pass in proto tcp to 1234:5678::/32 port 10000;"),
545 Ok(vec![filter::Rule {
546 action: filter::Action::Pass,
547 direction: filter::Direction::Incoming,
548 proto: filter::SocketProtocol::Tcp,
549 src_subnet: None,
550 src_subnet_invert_match: false,
551 src_port_range: filter::PortRange { start: 0, end: 0 },
552 dst_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
553 dst_subnet_invert_match: false,
554 dst_port_range: filter::PortRange { start: 10000, end: 10000 },
555 nic: 0,
556 log: false,
557 keep_state: false,
558 device_class: filter::DeviceClass::Any(filter::Empty {}),
559 }])
560 );
561 }
562
563 #[test]
564 fn test_rule_with_from_v6_address_port_to_v4_address_port() {
565 assert_eq!(
566 parse_str_to_rules(
567 "pass in proto tcp from 1234:5678::/32 port 10000 to 1.2.3.4/8 port 1000;"
568 ),
569 Err(Error::Invalid(InvalidReason::MixedIPVersions))
570 );
571 }
572
573 #[test]
574 fn test_rule_with_from_v6_address_port_to_v6_address_port() {
575 assert_eq!(
576 parse_str_to_rules(
577 "pass in proto tcp from 1234:5678::/32 port 10000 to 2345:6789::/32 port 1000;"
578 ),
579 Ok(vec![filter::Rule {
580 action: filter::Action::Pass,
581 direction: filter::Direction::Incoming,
582 proto: filter::SocketProtocol::Tcp,
583 src_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
584 src_subnet_invert_match: false,
585 src_port_range: filter::PortRange { start: 10000, end: 10000 },
586 dst_subnet: Some(Box::new(fidl_subnet!("2345:6789::/32"))),
587 dst_subnet_invert_match: false,
588 dst_port_range: filter::PortRange { start: 1000, end: 1000 },
589 nic: 0,
590 log: false,
591 keep_state: false,
592 device_class: filter::DeviceClass::Any(filter::Empty {}),
593 }])
594 );
595 }
596
597 #[test]
598 fn test_rule_with_log_no_state() {
599 assert_eq!(
600 parse_str_to_rules("pass in proto tcp log no state;"),
601 Ok(vec![filter::Rule {
602 action: filter::Action::Pass,
603 direction: filter::Direction::Incoming,
604 proto: filter::SocketProtocol::Tcp,
605 src_subnet: None,
606 src_subnet_invert_match: false,
607 src_port_range: filter::PortRange { start: 0, end: 0 },
608 dst_subnet: None,
609 dst_subnet_invert_match: false,
610 dst_port_range: filter::PortRange { start: 0, end: 0 },
611 nic: 0,
612 log: true,
613 keep_state: false,
614 device_class: filter::DeviceClass::Any(filter::Empty {}),
615 }])
616 );
617 }
618
619 #[test]
620 fn test_rule_with_keep_state() {
621 assert_eq!(
622 parse_str_to_rules("pass in proto tcp keep state;"),
623 Ok(vec![filter::Rule {
624 action: filter::Action::Pass,
625 direction: filter::Direction::Incoming,
626 proto: filter::SocketProtocol::Tcp,
627 src_subnet: None,
628 src_subnet_invert_match: false,
629 src_port_range: filter::PortRange { start: 0, end: 0 },
630 dst_subnet: None,
631 dst_subnet_invert_match: false,
632 dst_port_range: filter::PortRange { start: 0, end: 0 },
633 nic: 0,
634 log: false,
635 keep_state: true,
636 device_class: filter::DeviceClass::Any(filter::Empty {}),
637 }])
638 );
639 }
640
641 #[test]
642 fn test_rule_with_device_class() {
643 assert_eq!(
644 parse_str_to_rules("pass in proto tcp devclass ap;"),
645 Ok(vec![filter::Rule {
646 action: filter::Action::Pass,
647 direction: filter::Direction::Incoming,
648 proto: filter::SocketProtocol::Tcp,
649 src_subnet: None,
650 src_subnet_invert_match: false,
651 src_port_range: filter::PortRange { start: 0, end: 0 },
652 dst_subnet: None,
653 dst_subnet_invert_match: false,
654 dst_port_range: filter::PortRange { start: 0, end: 0 },
655 nic: 0,
656 log: false,
657 keep_state: false,
658 device_class: filter::DeviceClass::Match_(fhnet::PortClass::WlanAp),
659 }])
660 );
661 }
662
663 #[test]
664 fn test_rule_with_device_class_and_dst_range() {
665 assert_eq!(
666 parse_str_to_rules("pass in proto tcp devclass ap to range 1:2;"),
667 Ok(vec![filter::Rule {
668 action: filter::Action::Pass,
669 direction: filter::Direction::Incoming,
670 proto: filter::SocketProtocol::Tcp,
671 src_subnet: None,
672 src_subnet_invert_match: false,
673 src_port_range: filter::PortRange { start: 0, end: 0 },
674 dst_subnet: None,
675 dst_subnet_invert_match: false,
676 dst_port_range: filter::PortRange { start: 1, end: 2 },
677 nic: 0,
678 log: false,
679 keep_state: false,
680 device_class: filter::DeviceClass::Match_(fhnet::PortClass::WlanAp),
681 }])
682 );
683 }
684
685 #[test]
686 fn test_nat_rule_from_v4_subnet() {
687 assert_eq!(
688 parse_str_to_nat_rules("nat from 1.2.3.0/24 -> from 192.168.1.1;"),
689 Ok(vec![filter::Nat {
690 proto: filter::SocketProtocol::Any,
691 src_subnet: fidl_subnet!("1.2.3.0/24"),
692 outgoing_nic: 0,
693 }])
694 );
695 }
696
697 #[test]
698 fn test_nat_rule_with_proto_tcp_from_v4_subnet() {
699 assert_eq!(
700 parse_str_to_nat_rules("nat proto tcp from 1.2.3.0/24 -> from 192.168.1.1;"),
701 Ok(vec![filter::Nat {
702 proto: filter::SocketProtocol::Tcp,
703 src_subnet: fidl_subnet!("1.2.3.0/24"),
704 outgoing_nic: 0,
705 }])
706 );
707 }
708
709 #[test]
710 fn test_rdr_rule_with_to_v4_address_port_to_v4_address_port() {
711 assert_eq!(
712 parse_str_to_rdr_rules("rdr to 1.2.3.4 port 10000 -> to 192.168.1.1 port 20000;"),
713 Ok(vec![filter::Rdr {
714 proto: filter::SocketProtocol::Any,
715 dst_addr: fidl_ip!("1.2.3.4"),
716 dst_port_range: filter::PortRange { start: 10000, end: 10000 },
717 new_dst_addr: fidl_ip!("192.168.1.1"),
718 new_dst_port_range: filter::PortRange { start: 20000, end: 20000 },
719 nic: 0,
720 }])
721 );
722 }
723
724 #[test]
725 fn test_rdr_rule_with_proto_tcp_to_v4_address_port_to_v4_address_port() {
726 assert_eq!(
727 parse_str_to_rdr_rules(
728 "rdr proto tcp to 1.2.3.4 port 10000 -> to 192.168.1.1 port 20000;"
729 ),
730 Ok(vec![filter::Rdr {
731 proto: filter::SocketProtocol::Tcp,
732 dst_addr: fidl_ip!("1.2.3.4"),
733 dst_port_range: filter::PortRange { start: 10000, end: 10000 },
734 new_dst_addr: fidl_ip!("192.168.1.1"),
735 new_dst_port_range: filter::PortRange { start: 20000, end: 20000 },
736 nic: 0,
737 }])
738 );
739 }
740
741 #[test]
742 fn test_rdr_rule_with_to_v4_address_range_to_v4_address_range() {
743 assert_eq!(
744 parse_str_to_rdr_rules(
745 "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 20000:20005;"
746 ),
747 Ok(vec![filter::Rdr {
748 proto: filter::SocketProtocol::Tcp,
749 dst_addr: fidl_ip!("1.2.3.4"),
750 dst_port_range: filter::PortRange { start: 10000, end: 10005 },
751 new_dst_addr: fidl_ip!("192.168.1.1"),
752 new_dst_port_range: filter::PortRange { start: 20000, end: 20005 },
753 nic: 0,
754 }])
755 );
756 }
757
758 #[test]
759 fn test_rdr_rule_with_to_v4_address_range_to_v4_address_invalid_range() {
760 assert_eq!(
761 parse_str_to_rdr_rules(
762 "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 20005:20000;"
763 ),
764 Err(Error::Invalid(InvalidReason::InvalidPortRange))
765 );
766 }
767
768 #[test]
769 fn test_rdr_rule_with_to_v4_address_range_to_v4_address_port_range_length_mismatch() {
770 assert_eq!(
771 parse_str_to_rdr_rules(
772 "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 20000:20003;"
773 ),
774 Err(Error::Invalid(InvalidReason::PortRangeLengthMismatch))
775 );
776 }
777
778 #[test]
779 fn test_rdr_rule_with_to_v4_address_range_to_v6_address_range() {
780 assert_eq!(
781 parse_str_to_rdr_rules(
782 "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 1234:5678:: range 20000:20005;"
783 ),
784 Err(Error::Invalid(InvalidReason::MixedIPVersions))
785 );
786 }
787
788 #[test]
789 fn test_rdr_rule_with_to_v6_address_range_to_v6_address_range() {
790 assert_eq!(
791 parse_str_to_rdr_rules(
792 "rdr proto tcp to 1234:5678:: range 10000:10005 -> to 2345:6789:: range 20000:20005;"
793 ),
794 Ok(vec![filter::Rdr {
795 proto: filter::SocketProtocol::Tcp,
796 dst_addr: fidl_ip!("1234:5678::"),
797 dst_port_range: filter::PortRange { start: 10000, end: 10005 },
798 new_dst_addr: fidl_ip!("2345:6789::"),
799 new_dst_port_range: filter::PortRange { start: 20000, end: 20005 },
800 nic: 0,
801 }])
802 );
803 }
804}