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