netfilter/
parser_deprecated.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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, // no state by default
136    }
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, // TODO: Support NICID (currently always 0 (= any))
163        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, // TODO: Support NICID.
180    })
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, // TODO: Support NICID.
200    })
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}