Skip to main content

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::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, // no state by default
135    }
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, // TODO: Support NICID (currently always 0 (= any))
162        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, // TODO: Support NICID.
179    })
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, // TODO: Support NICID.
199    })
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}