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 == 0 && range.end != 0) || 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_invalid_range_1() {
447 assert_eq!(
448 parse_str_to_rules("pass in proto tcp from range 0:5;"),
449 Err(Error::Invalid(InvalidReason::InvalidPortRange))
450 );
451 }
452
453 #[test]
454 fn test_rule_with_from_invalid_range_2() {
455 assert_eq!(
456 parse_str_to_rules("pass in proto tcp from range 10005:10000;"),
457 Err(Error::Invalid(InvalidReason::InvalidPortRange))
458 );
459 }
460
461 #[test]
462 fn test_rule_with_from_v4_address_port() {
463 assert_eq!(
464 parse_str_to_rules("pass in proto tcp from 1.2.3.4/24 port 10000;"),
465 Ok(vec![filter::Rule {
466 action: filter::Action::Pass,
467 direction: filter::Direction::Incoming,
468 proto: filter::SocketProtocol::Tcp,
469 src_subnet: Some(Box::new(fidl_subnet!("1.2.3.4/24"))),
470 src_subnet_invert_match: false,
471 src_port_range: filter::PortRange { start: 10000, end: 10000 },
472 dst_subnet: None,
473 dst_subnet_invert_match: false,
474 dst_port_range: filter::PortRange { start: 0, end: 0 },
475 nic: 0,
476 log: false,
477 keep_state: false,
478 device_class: filter::DeviceClass::Any(filter::Empty {}),
479 }])
480 );
481 }
482
483 #[test]
484 fn test_rule_with_from_not_v4_address_port() {
485 assert_eq!(
486 parse_str_to_rules("pass in proto tcp from !1.2.3.4/24 port 10000;"),
487 Ok(vec![filter::Rule {
488 action: filter::Action::Pass,
489 direction: filter::Direction::Incoming,
490 proto: filter::SocketProtocol::Tcp,
491 src_subnet: Some(Box::new(fidl_subnet!("1.2.3.4/24"))),
492 src_subnet_invert_match: true,
493 src_port_range: filter::PortRange { start: 10000, end: 10000 },
494 dst_subnet: None,
495 dst_subnet_invert_match: false,
496 dst_port_range: filter::PortRange { start: 0, end: 0 },
497 nic: 0,
498 log: false,
499 keep_state: false,
500 device_class: filter::DeviceClass::Any(filter::Empty {}),
501 }])
502 );
503 }
504
505 #[test]
506 fn test_rule_with_from_v6_address_port() {
507 assert_eq!(
508 parse_str_to_rules("pass in proto tcp from 1234:5678::/32 port 10000;"),
509 Ok(vec![filter::Rule {
510 action: filter::Action::Pass,
511 direction: filter::Direction::Incoming,
512 proto: filter::SocketProtocol::Tcp,
513 src_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
514 src_subnet_invert_match: false,
515 src_port_range: filter::PortRange { start: 10000, end: 10000 },
516 dst_subnet: None,
517 dst_subnet_invert_match: false,
518 dst_port_range: filter::PortRange { start: 0, end: 0 },
519 nic: 0,
520 log: false,
521 keep_state: false,
522 device_class: filter::DeviceClass::Any(filter::Empty {}),
523 }])
524 );
525 }
526
527 #[test]
528 fn test_rule_with_to_v6_address_port() {
529 assert_eq!(
530 parse_str_to_rules("pass in proto tcp to 1234:5678::/32 port 10000;"),
531 Ok(vec![filter::Rule {
532 action: filter::Action::Pass,
533 direction: filter::Direction::Incoming,
534 proto: filter::SocketProtocol::Tcp,
535 src_subnet: None,
536 src_subnet_invert_match: false,
537 src_port_range: filter::PortRange { start: 0, end: 0 },
538 dst_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
539 dst_subnet_invert_match: false,
540 dst_port_range: filter::PortRange { start: 10000, end: 10000 },
541 nic: 0,
542 log: false,
543 keep_state: false,
544 device_class: filter::DeviceClass::Any(filter::Empty {}),
545 }])
546 );
547 }
548
549 #[test]
550 fn test_rule_with_from_v6_address_port_to_v4_address_port() {
551 assert_eq!(
552 parse_str_to_rules(
553 "pass in proto tcp from 1234:5678::/32 port 10000 to 1.2.3.4/8 port 1000;"
554 ),
555 Err(Error::Invalid(InvalidReason::MixedIPVersions))
556 );
557 }
558
559 #[test]
560 fn test_rule_with_from_v6_address_port_to_v6_address_port() {
561 assert_eq!(
562 parse_str_to_rules(
563 "pass in proto tcp from 1234:5678::/32 port 10000 to 2345:6789::/32 port 1000;"
564 ),
565 Ok(vec![filter::Rule {
566 action: filter::Action::Pass,
567 direction: filter::Direction::Incoming,
568 proto: filter::SocketProtocol::Tcp,
569 src_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
570 src_subnet_invert_match: false,
571 src_port_range: filter::PortRange { start: 10000, end: 10000 },
572 dst_subnet: Some(Box::new(fidl_subnet!("2345:6789::/32"))),
573 dst_subnet_invert_match: false,
574 dst_port_range: filter::PortRange { start: 1000, end: 1000 },
575 nic: 0,
576 log: false,
577 keep_state: false,
578 device_class: filter::DeviceClass::Any(filter::Empty {}),
579 }])
580 );
581 }
582
583 #[test]
584 fn test_rule_with_log_no_state() {
585 assert_eq!(
586 parse_str_to_rules("pass in proto tcp log no state;"),
587 Ok(vec![filter::Rule {
588 action: filter::Action::Pass,
589 direction: filter::Direction::Incoming,
590 proto: filter::SocketProtocol::Tcp,
591 src_subnet: None,
592 src_subnet_invert_match: false,
593 src_port_range: filter::PortRange { start: 0, end: 0 },
594 dst_subnet: None,
595 dst_subnet_invert_match: false,
596 dst_port_range: filter::PortRange { start: 0, end: 0 },
597 nic: 0,
598 log: true,
599 keep_state: false,
600 device_class: filter::DeviceClass::Any(filter::Empty {}),
601 }])
602 );
603 }
604
605 #[test]
606 fn test_rule_with_keep_state() {
607 assert_eq!(
608 parse_str_to_rules("pass in proto tcp keep state;"),
609 Ok(vec![filter::Rule {
610 action: filter::Action::Pass,
611 direction: filter::Direction::Incoming,
612 proto: filter::SocketProtocol::Tcp,
613 src_subnet: None,
614 src_subnet_invert_match: false,
615 src_port_range: filter::PortRange { start: 0, end: 0 },
616 dst_subnet: None,
617 dst_subnet_invert_match: false,
618 dst_port_range: filter::PortRange { start: 0, end: 0 },
619 nic: 0,
620 log: false,
621 keep_state: true,
622 device_class: filter::DeviceClass::Any(filter::Empty {}),
623 }])
624 );
625 }
626
627 #[test]
628 fn test_rule_with_device_class() {
629 assert_eq!(
630 parse_str_to_rules("pass in proto tcp devclass ap;"),
631 Ok(vec![filter::Rule {
632 action: filter::Action::Pass,
633 direction: filter::Direction::Incoming,
634 proto: filter::SocketProtocol::Tcp,
635 src_subnet: None,
636 src_subnet_invert_match: false,
637 src_port_range: filter::PortRange { start: 0, end: 0 },
638 dst_subnet: None,
639 dst_subnet_invert_match: false,
640 dst_port_range: filter::PortRange { start: 0, end: 0 },
641 nic: 0,
642 log: false,
643 keep_state: false,
644 device_class: filter::DeviceClass::Match_(fhnet::PortClass::WlanAp),
645 }])
646 );
647 }
648
649 #[test]
650 fn test_rule_with_device_class_and_dst_range() {
651 assert_eq!(
652 parse_str_to_rules("pass in proto tcp devclass ap to range 1:2;"),
653 Ok(vec![filter::Rule {
654 action: filter::Action::Pass,
655 direction: filter::Direction::Incoming,
656 proto: filter::SocketProtocol::Tcp,
657 src_subnet: None,
658 src_subnet_invert_match: false,
659 src_port_range: filter::PortRange { start: 0, end: 0 },
660 dst_subnet: None,
661 dst_subnet_invert_match: false,
662 dst_port_range: filter::PortRange { start: 1, end: 2 },
663 nic: 0,
664 log: false,
665 keep_state: false,
666 device_class: filter::DeviceClass::Match_(fhnet::PortClass::WlanAp),
667 }])
668 );
669 }
670
671 #[test]
672 fn test_nat_rule_from_v4_subnet() {
673 assert_eq!(
674 parse_str_to_nat_rules("nat from 1.2.3.0/24 -> from 192.168.1.1;"),
675 Ok(vec![filter::Nat {
676 proto: filter::SocketProtocol::Any,
677 src_subnet: fidl_subnet!("1.2.3.0/24"),
678 outgoing_nic: 0,
679 }])
680 );
681 }
682
683 #[test]
684 fn test_nat_rule_with_proto_tcp_from_v4_subnet() {
685 assert_eq!(
686 parse_str_to_nat_rules("nat proto tcp from 1.2.3.0/24 -> from 192.168.1.1;"),
687 Ok(vec![filter::Nat {
688 proto: filter::SocketProtocol::Tcp,
689 src_subnet: fidl_subnet!("1.2.3.0/24"),
690 outgoing_nic: 0,
691 }])
692 );
693 }
694
695 #[test]
696 fn test_rdr_rule_with_to_v4_address_port_to_v4_address_port() {
697 assert_eq!(
698 parse_str_to_rdr_rules("rdr to 1.2.3.4 port 10000 -> to 192.168.1.1 port 20000;"),
699 Ok(vec![filter::Rdr {
700 proto: filter::SocketProtocol::Any,
701 dst_addr: fidl_ip!("1.2.3.4"),
702 dst_port_range: filter::PortRange { start: 10000, end: 10000 },
703 new_dst_addr: fidl_ip!("192.168.1.1"),
704 new_dst_port_range: filter::PortRange { start: 20000, end: 20000 },
705 nic: 0,
706 }])
707 );
708 }
709
710 #[test]
711 fn test_rdr_rule_with_proto_tcp_to_v4_address_port_to_v4_address_port() {
712 assert_eq!(
713 parse_str_to_rdr_rules(
714 "rdr proto tcp to 1.2.3.4 port 10000 -> to 192.168.1.1 port 20000;"
715 ),
716 Ok(vec![filter::Rdr {
717 proto: filter::SocketProtocol::Tcp,
718 dst_addr: fidl_ip!("1.2.3.4"),
719 dst_port_range: filter::PortRange { start: 10000, end: 10000 },
720 new_dst_addr: fidl_ip!("192.168.1.1"),
721 new_dst_port_range: filter::PortRange { start: 20000, end: 20000 },
722 nic: 0,
723 }])
724 );
725 }
726
727 #[test]
728 fn test_rdr_rule_with_to_v4_address_range_to_v4_address_range() {
729 assert_eq!(
730 parse_str_to_rdr_rules(
731 "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 20000:20005;"
732 ),
733 Ok(vec![filter::Rdr {
734 proto: filter::SocketProtocol::Tcp,
735 dst_addr: fidl_ip!("1.2.3.4"),
736 dst_port_range: filter::PortRange { start: 10000, end: 10005 },
737 new_dst_addr: fidl_ip!("192.168.1.1"),
738 new_dst_port_range: filter::PortRange { start: 20000, end: 20005 },
739 nic: 0,
740 }])
741 );
742 }
743
744 #[test]
745 fn test_rdr_rule_with_to_v4_address_range_to_v4_address_invalid_range() {
746 assert_eq!(
747 parse_str_to_rdr_rules(
748 "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 0:5;"
749 ),
750 Err(Error::Invalid(InvalidReason::InvalidPortRange))
751 );
752 }
753
754 #[test]
755 fn test_rdr_rule_with_to_v4_address_range_to_v4_address_port_range_length_mismatch() {
756 assert_eq!(
757 parse_str_to_rdr_rules(
758 "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 20000:20003;"
759 ),
760 Err(Error::Invalid(InvalidReason::PortRangeLengthMismatch))
761 );
762 }
763
764 #[test]
765 fn test_rdr_rule_with_to_v4_address_range_to_v6_address_range() {
766 assert_eq!(
767 parse_str_to_rdr_rules(
768 "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 1234:5678:: range 20000:20005;"
769 ),
770 Err(Error::Invalid(InvalidReason::MixedIPVersions))
771 );
772 }
773
774 #[test]
775 fn test_rdr_rule_with_to_v6_address_range_to_v6_address_range() {
776 assert_eq!(
777 parse_str_to_rdr_rules(
778 "rdr proto tcp to 1234:5678:: range 10000:10005 -> to 2345:6789:: range 20000:20005;"
779 ),
780 Ok(vec![filter::Rdr {
781 proto: filter::SocketProtocol::Tcp,
782 dst_addr: fidl_ip!("1234:5678::"),
783 dst_port_range: filter::PortRange { start: 10000, end: 10005 },
784 new_dst_addr: fidl_ip!("2345:6789::"),
785 new_dst_port_range: filter::PortRange { start: 20000, end: 20005 },
786 nic: 0,
787 }])
788 );
789 }
790}