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 > range.end {
204        return Err(Error::Invalid(InvalidReason::InvalidPortRange));
205    }
206    Ok(())
207}
208
209fn port_range_length(range: &filter::PortRange) -> Result<u16, Error> {
210    validate_port_range(&range)?;
211    Ok(range.end - range.start)
212}
213
214fn validate_rule(rule: &filter::Rule) -> Result<(), Error> {
215    if let (Some(src_subnet), Some(dst_subnet)) = (&rule.src_subnet, &rule.dst_subnet) {
216        if !util::ip_version_eq(&src_subnet.addr, &dst_subnet.addr) {
217            return Err(Error::Invalid(InvalidReason::MixedIPVersions));
218        }
219    }
220    validate_port_range(&rule.src_port_range)?;
221    validate_port_range(&rule.dst_port_range)?;
222
223    Ok(())
224}
225
226fn validate_rdr(rdr: &filter::Rdr) -> Result<(), Error> {
227    if !util::ip_version_eq(&rdr.dst_addr, &rdr.new_dst_addr) {
228        return Err(Error::Invalid(InvalidReason::MixedIPVersions));
229    }
230    if port_range_length(&rdr.dst_port_range)? != port_range_length(&rdr.new_dst_port_range)? {
231        return Err(Error::Invalid(InvalidReason::PortRangeLengthMismatch));
232    }
233
234    Ok(())
235}
236
237pub fn parse_str_to_rules(line: &str) -> Result<Vec<filter::Rule>, Error> {
238    let mut pairs =
239        FilterRuleParser::parse(Rule::rules, &line).map_err(|err| Error::Pest(Box::new(err)))?;
240    let mut rules = Vec::new();
241    for filter_rule in pairs.next().unwrap().into_inner() {
242        match filter_rule.as_rule() {
243            Rule::rule => {
244                let rule = parse_rule(filter_rule)?;
245                validate_rule(&rule)?;
246                rules.push(rule);
247            }
248            Rule::EOI => (),
249            _ => unreachable!("rule must only have a rule case"),
250        }
251    }
252    Ok(rules)
253}
254
255pub fn parse_str_to_nat_rules(line: &str) -> Result<Vec<filter::Nat>, Error> {
256    let mut pairs = FilterRuleParser::parse(Rule::nat_rules, &line)
257        .map_err(|err| Error::Pest(Box::new(err)))?;
258    let mut nat_rules = Vec::new();
259    for filter_rule in pairs.next().unwrap().into_inner() {
260        match filter_rule.as_rule() {
261            Rule::nat => {
262                let nat = parse_nat(filter_rule)?;
263                nat_rules.push(nat);
264            }
265            Rule::EOI => (),
266            _ => unreachable!("nat must only have a nat case"),
267        }
268    }
269    Ok(nat_rules)
270}
271
272pub fn parse_str_to_rdr_rules(line: &str) -> Result<Vec<filter::Rdr>, Error> {
273    let mut pairs = FilterRuleParser::parse(Rule::rdr_rules, &line)
274        .map_err(|err| Error::Pest(Box::new(err)))?;
275    let mut rdr_rules = Vec::new();
276    for filter_rule in pairs.next().unwrap().into_inner() {
277        match filter_rule.as_rule() {
278            Rule::rdr => {
279                let rdr = parse_rdr(filter_rule)?;
280                validate_rdr(&rdr)?;
281                rdr_rules.push(rdr);
282            }
283            Rule::EOI => (),
284            _ => unreachable!("rdr must only have a rdr case"),
285        }
286    }
287    Ok(rdr_rules)
288}
289
290#[cfg(test)]
291mod test {
292    use super::*;
293
294    use net_declare::{fidl_ip, fidl_subnet};
295
296    #[test]
297    fn test_rule_with_proto_any() {
298        assert_eq!(
299            parse_str_to_rules("pass in;"),
300            Ok(vec![filter::Rule {
301                action: filter::Action::Pass,
302                direction: filter::Direction::Incoming,
303                proto: filter::SocketProtocol::Any,
304                src_subnet: None,
305                src_subnet_invert_match: false,
306                src_port_range: filter::PortRange { start: 0, end: 0 },
307                dst_subnet: None,
308                dst_subnet_invert_match: false,
309                dst_port_range: filter::PortRange { start: 0, end: 0 },
310                nic: 0,
311                log: false,
312                keep_state: false,
313                device_class: filter::DeviceClass::Any(filter::Empty {}),
314            }])
315        );
316    }
317
318    #[test]
319    fn test_rule_with_proto_tcp() {
320        assert_eq!(
321            parse_str_to_rules("pass in proto tcp;"),
322            Ok(vec![filter::Rule {
323                action: filter::Action::Pass,
324                direction: filter::Direction::Incoming,
325                proto: filter::SocketProtocol::Tcp,
326                src_subnet: None,
327                src_subnet_invert_match: false,
328                src_port_range: filter::PortRange { start: 0, end: 0 },
329                dst_subnet: None,
330                dst_subnet_invert_match: false,
331                dst_port_range: filter::PortRange { start: 0, end: 0 },
332                nic: 0,
333                log: false,
334                keep_state: false,
335                device_class: filter::DeviceClass::Any(filter::Empty {}),
336            }])
337        );
338    }
339
340    #[test]
341    fn test_multiple_rules() {
342        assert_eq!(
343            parse_str_to_rules("pass in proto tcp; drop out proto udp;"),
344            Ok(vec![
345                filter::Rule {
346                    action: filter::Action::Pass,
347                    direction: filter::Direction::Incoming,
348                    proto: filter::SocketProtocol::Tcp,
349                    src_subnet: None,
350                    src_subnet_invert_match: false,
351                    src_port_range: filter::PortRange { start: 0, end: 0 },
352                    dst_subnet: None,
353                    dst_subnet_invert_match: false,
354                    dst_port_range: filter::PortRange { start: 0, end: 0 },
355                    nic: 0,
356                    log: false,
357                    keep_state: false,
358                    device_class: filter::DeviceClass::Any(filter::Empty {}),
359                },
360                filter::Rule {
361                    action: filter::Action::Drop,
362                    direction: filter::Direction::Outgoing,
363                    proto: filter::SocketProtocol::Udp,
364                    src_subnet: None,
365                    src_subnet_invert_match: false,
366                    src_port_range: filter::PortRange { start: 0, end: 0 },
367                    dst_subnet: None,
368                    dst_subnet_invert_match: false,
369                    dst_port_range: filter::PortRange { start: 0, end: 0 },
370                    nic: 0,
371                    log: false,
372                    keep_state: false,
373                    device_class: filter::DeviceClass::Any(filter::Empty {}),
374                },
375            ])
376        );
377    }
378
379    #[test]
380    fn test_rule_with_from_v4_address() {
381        assert_eq!(
382            parse_str_to_rules("pass in proto tcp from 1.2.3.4/24;"),
383            Ok(vec![filter::Rule {
384                action: filter::Action::Pass,
385                direction: filter::Direction::Incoming,
386                proto: filter::SocketProtocol::Tcp,
387                src_subnet: Some(Box::new(fidl_subnet!("1.2.3.4/24"))),
388                src_subnet_invert_match: false,
389                src_port_range: filter::PortRange { start: 0, end: 0 },
390                dst_subnet: None,
391                dst_subnet_invert_match: false,
392                dst_port_range: filter::PortRange { start: 0, end: 0 },
393                nic: 0,
394                log: false,
395                keep_state: false,
396                device_class: filter::DeviceClass::Any(filter::Empty {}),
397            }])
398        );
399    }
400
401    #[test]
402    fn test_rule_with_from_port() {
403        assert_eq!(
404            parse_str_to_rules("pass in proto tcp from port 10000;"),
405            Ok(vec![filter::Rule {
406                action: filter::Action::Pass,
407                direction: filter::Direction::Incoming,
408                proto: filter::SocketProtocol::Tcp,
409                src_subnet: None,
410                src_subnet_invert_match: false,
411                src_port_range: filter::PortRange { start: 10000, end: 10000 },
412                dst_subnet: None,
413                dst_subnet_invert_match: false,
414                dst_port_range: filter::PortRange { start: 0, end: 0 },
415                nic: 0,
416                log: false,
417                keep_state: false,
418                device_class: filter::DeviceClass::Any(filter::Empty {}),
419            }])
420        );
421    }
422
423    #[test]
424    fn test_rule_with_from_range() {
425        assert_eq!(
426            parse_str_to_rules("pass in proto tcp from range 10000:10010;"),
427            Ok(vec![filter::Rule {
428                action: filter::Action::Pass,
429                direction: filter::Direction::Incoming,
430                proto: filter::SocketProtocol::Tcp,
431                src_subnet: None,
432                src_subnet_invert_match: false,
433                src_port_range: filter::PortRange { start: 10000, end: 10010 },
434                dst_subnet: None,
435                dst_subnet_invert_match: false,
436                dst_port_range: filter::PortRange { start: 0, end: 0 },
437                nic: 0,
438                log: false,
439                keep_state: false,
440                device_class: filter::DeviceClass::Any(filter::Empty {}),
441            }])
442        );
443    }
444
445    #[test]
446    fn test_rule_with_from_range_2() {
447        assert_eq!(
448            parse_str_to_rules("pass in proto tcp from range 0:5;"),
449            Ok(vec![filter::Rule {
450                action: filter::Action::Pass,
451                direction: filter::Direction::Incoming,
452                proto: filter::SocketProtocol::Tcp,
453                src_subnet: None,
454                src_subnet_invert_match: false,
455                src_port_range: filter::PortRange { start: 0, end: 5 },
456                dst_subnet: None,
457                dst_subnet_invert_match: false,
458                dst_port_range: filter::PortRange { start: 0, end: 0 },
459                nic: 0,
460                log: false,
461                keep_state: false,
462                device_class: filter::DeviceClass::Any(filter::Empty {}),
463            }])
464        );
465    }
466
467    #[test]
468    fn test_rule_with_from_invalid_range() {
469        assert_eq!(
470            parse_str_to_rules("pass in proto tcp from range 10005:10000;"),
471            Err(Error::Invalid(InvalidReason::InvalidPortRange))
472        );
473    }
474
475    #[test]
476    fn test_rule_with_from_v4_address_port() {
477        assert_eq!(
478            parse_str_to_rules("pass in proto tcp from 1.2.3.4/24 port 10000;"),
479            Ok(vec![filter::Rule {
480                action: filter::Action::Pass,
481                direction: filter::Direction::Incoming,
482                proto: filter::SocketProtocol::Tcp,
483                src_subnet: Some(Box::new(fidl_subnet!("1.2.3.4/24"))),
484                src_subnet_invert_match: false,
485                src_port_range: filter::PortRange { start: 10000, end: 10000 },
486                dst_subnet: None,
487                dst_subnet_invert_match: false,
488                dst_port_range: filter::PortRange { start: 0, end: 0 },
489                nic: 0,
490                log: false,
491                keep_state: false,
492                device_class: filter::DeviceClass::Any(filter::Empty {}),
493            }])
494        );
495    }
496
497    #[test]
498    fn test_rule_with_from_not_v4_address_port() {
499        assert_eq!(
500            parse_str_to_rules("pass in proto tcp from !1.2.3.4/24 port 10000;"),
501            Ok(vec![filter::Rule {
502                action: filter::Action::Pass,
503                direction: filter::Direction::Incoming,
504                proto: filter::SocketProtocol::Tcp,
505                src_subnet: Some(Box::new(fidl_subnet!("1.2.3.4/24"))),
506                src_subnet_invert_match: true,
507                src_port_range: filter::PortRange { start: 10000, end: 10000 },
508                dst_subnet: None,
509                dst_subnet_invert_match: false,
510                dst_port_range: filter::PortRange { start: 0, end: 0 },
511                nic: 0,
512                log: false,
513                keep_state: false,
514                device_class: filter::DeviceClass::Any(filter::Empty {}),
515            }])
516        );
517    }
518
519    #[test]
520    fn test_rule_with_from_v6_address_port() {
521        assert_eq!(
522            parse_str_to_rules("pass in proto tcp from 1234:5678::/32 port 10000;"),
523            Ok(vec![filter::Rule {
524                action: filter::Action::Pass,
525                direction: filter::Direction::Incoming,
526                proto: filter::SocketProtocol::Tcp,
527                src_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
528                src_subnet_invert_match: false,
529                src_port_range: filter::PortRange { start: 10000, end: 10000 },
530                dst_subnet: None,
531                dst_subnet_invert_match: false,
532                dst_port_range: filter::PortRange { start: 0, end: 0 },
533                nic: 0,
534                log: false,
535                keep_state: false,
536                device_class: filter::DeviceClass::Any(filter::Empty {}),
537            }])
538        );
539    }
540
541    #[test]
542    fn test_rule_with_to_v6_address_port() {
543        assert_eq!(
544            parse_str_to_rules("pass in proto tcp to 1234:5678::/32 port 10000;"),
545            Ok(vec![filter::Rule {
546                action: filter::Action::Pass,
547                direction: filter::Direction::Incoming,
548                proto: filter::SocketProtocol::Tcp,
549                src_subnet: None,
550                src_subnet_invert_match: false,
551                src_port_range: filter::PortRange { start: 0, end: 0 },
552                dst_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
553                dst_subnet_invert_match: false,
554                dst_port_range: filter::PortRange { start: 10000, end: 10000 },
555                nic: 0,
556                log: false,
557                keep_state: false,
558                device_class: filter::DeviceClass::Any(filter::Empty {}),
559            }])
560        );
561    }
562
563    #[test]
564    fn test_rule_with_from_v6_address_port_to_v4_address_port() {
565        assert_eq!(
566            parse_str_to_rules(
567                "pass in proto tcp from 1234:5678::/32 port 10000 to 1.2.3.4/8 port 1000;"
568            ),
569            Err(Error::Invalid(InvalidReason::MixedIPVersions))
570        );
571    }
572
573    #[test]
574    fn test_rule_with_from_v6_address_port_to_v6_address_port() {
575        assert_eq!(
576            parse_str_to_rules(
577                "pass in proto tcp from 1234:5678::/32 port 10000 to 2345:6789::/32 port 1000;"
578            ),
579            Ok(vec![filter::Rule {
580                action: filter::Action::Pass,
581                direction: filter::Direction::Incoming,
582                proto: filter::SocketProtocol::Tcp,
583                src_subnet: Some(Box::new(fidl_subnet!("1234:5678::/32"))),
584                src_subnet_invert_match: false,
585                src_port_range: filter::PortRange { start: 10000, end: 10000 },
586                dst_subnet: Some(Box::new(fidl_subnet!("2345:6789::/32"))),
587                dst_subnet_invert_match: false,
588                dst_port_range: filter::PortRange { start: 1000, end: 1000 },
589                nic: 0,
590                log: false,
591                keep_state: false,
592                device_class: filter::DeviceClass::Any(filter::Empty {}),
593            }])
594        );
595    }
596
597    #[test]
598    fn test_rule_with_log_no_state() {
599        assert_eq!(
600            parse_str_to_rules("pass in proto tcp log no state;"),
601            Ok(vec![filter::Rule {
602                action: filter::Action::Pass,
603                direction: filter::Direction::Incoming,
604                proto: filter::SocketProtocol::Tcp,
605                src_subnet: None,
606                src_subnet_invert_match: false,
607                src_port_range: filter::PortRange { start: 0, end: 0 },
608                dst_subnet: None,
609                dst_subnet_invert_match: false,
610                dst_port_range: filter::PortRange { start: 0, end: 0 },
611                nic: 0,
612                log: true,
613                keep_state: false,
614                device_class: filter::DeviceClass::Any(filter::Empty {}),
615            }])
616        );
617    }
618
619    #[test]
620    fn test_rule_with_keep_state() {
621        assert_eq!(
622            parse_str_to_rules("pass in proto tcp keep state;"),
623            Ok(vec![filter::Rule {
624                action: filter::Action::Pass,
625                direction: filter::Direction::Incoming,
626                proto: filter::SocketProtocol::Tcp,
627                src_subnet: None,
628                src_subnet_invert_match: false,
629                src_port_range: filter::PortRange { start: 0, end: 0 },
630                dst_subnet: None,
631                dst_subnet_invert_match: false,
632                dst_port_range: filter::PortRange { start: 0, end: 0 },
633                nic: 0,
634                log: false,
635                keep_state: true,
636                device_class: filter::DeviceClass::Any(filter::Empty {}),
637            }])
638        );
639    }
640
641    #[test]
642    fn test_rule_with_device_class() {
643        assert_eq!(
644            parse_str_to_rules("pass in proto tcp devclass ap;"),
645            Ok(vec![filter::Rule {
646                action: filter::Action::Pass,
647                direction: filter::Direction::Incoming,
648                proto: filter::SocketProtocol::Tcp,
649                src_subnet: None,
650                src_subnet_invert_match: false,
651                src_port_range: filter::PortRange { start: 0, end: 0 },
652                dst_subnet: None,
653                dst_subnet_invert_match: false,
654                dst_port_range: filter::PortRange { start: 0, end: 0 },
655                nic: 0,
656                log: false,
657                keep_state: false,
658                device_class: filter::DeviceClass::Match_(fhnet::PortClass::WlanAp),
659            }])
660        );
661    }
662
663    #[test]
664    fn test_rule_with_device_class_and_dst_range() {
665        assert_eq!(
666            parse_str_to_rules("pass in proto tcp devclass ap to range 1:2;"),
667            Ok(vec![filter::Rule {
668                action: filter::Action::Pass,
669                direction: filter::Direction::Incoming,
670                proto: filter::SocketProtocol::Tcp,
671                src_subnet: None,
672                src_subnet_invert_match: false,
673                src_port_range: filter::PortRange { start: 0, end: 0 },
674                dst_subnet: None,
675                dst_subnet_invert_match: false,
676                dst_port_range: filter::PortRange { start: 1, end: 2 },
677                nic: 0,
678                log: false,
679                keep_state: false,
680                device_class: filter::DeviceClass::Match_(fhnet::PortClass::WlanAp),
681            }])
682        );
683    }
684
685    #[test]
686    fn test_nat_rule_from_v4_subnet() {
687        assert_eq!(
688            parse_str_to_nat_rules("nat from 1.2.3.0/24 -> from 192.168.1.1;"),
689            Ok(vec![filter::Nat {
690                proto: filter::SocketProtocol::Any,
691                src_subnet: fidl_subnet!("1.2.3.0/24"),
692                outgoing_nic: 0,
693            }])
694        );
695    }
696
697    #[test]
698    fn test_nat_rule_with_proto_tcp_from_v4_subnet() {
699        assert_eq!(
700            parse_str_to_nat_rules("nat proto tcp from 1.2.3.0/24 -> from 192.168.1.1;"),
701            Ok(vec![filter::Nat {
702                proto: filter::SocketProtocol::Tcp,
703                src_subnet: fidl_subnet!("1.2.3.0/24"),
704                outgoing_nic: 0,
705            }])
706        );
707    }
708
709    #[test]
710    fn test_rdr_rule_with_to_v4_address_port_to_v4_address_port() {
711        assert_eq!(
712            parse_str_to_rdr_rules("rdr to 1.2.3.4 port 10000 -> to 192.168.1.1 port 20000;"),
713            Ok(vec![filter::Rdr {
714                proto: filter::SocketProtocol::Any,
715                dst_addr: fidl_ip!("1.2.3.4"),
716                dst_port_range: filter::PortRange { start: 10000, end: 10000 },
717                new_dst_addr: fidl_ip!("192.168.1.1"),
718                new_dst_port_range: filter::PortRange { start: 20000, end: 20000 },
719                nic: 0,
720            }])
721        );
722    }
723
724    #[test]
725    fn test_rdr_rule_with_proto_tcp_to_v4_address_port_to_v4_address_port() {
726        assert_eq!(
727            parse_str_to_rdr_rules(
728                "rdr proto tcp to 1.2.3.4 port 10000 -> to 192.168.1.1 port 20000;"
729            ),
730            Ok(vec![filter::Rdr {
731                proto: filter::SocketProtocol::Tcp,
732                dst_addr: fidl_ip!("1.2.3.4"),
733                dst_port_range: filter::PortRange { start: 10000, end: 10000 },
734                new_dst_addr: fidl_ip!("192.168.1.1"),
735                new_dst_port_range: filter::PortRange { start: 20000, end: 20000 },
736                nic: 0,
737            }])
738        );
739    }
740
741    #[test]
742    fn test_rdr_rule_with_to_v4_address_range_to_v4_address_range() {
743        assert_eq!(
744            parse_str_to_rdr_rules(
745                "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 20000:20005;"
746            ),
747            Ok(vec![filter::Rdr {
748                proto: filter::SocketProtocol::Tcp,
749                dst_addr: fidl_ip!("1.2.3.4"),
750                dst_port_range: filter::PortRange { start: 10000, end: 10005 },
751                new_dst_addr: fidl_ip!("192.168.1.1"),
752                new_dst_port_range: filter::PortRange { start: 20000, end: 20005 },
753                nic: 0,
754            }])
755        );
756    }
757
758    #[test]
759    fn test_rdr_rule_with_to_v4_address_range_to_v4_address_invalid_range() {
760        assert_eq!(
761            parse_str_to_rdr_rules(
762                "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 20005:20000;"
763            ),
764            Err(Error::Invalid(InvalidReason::InvalidPortRange))
765        );
766    }
767
768    #[test]
769    fn test_rdr_rule_with_to_v4_address_range_to_v4_address_port_range_length_mismatch() {
770        assert_eq!(
771            parse_str_to_rdr_rules(
772                "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 192.168.1.1 range 20000:20003;"
773            ),
774            Err(Error::Invalid(InvalidReason::PortRangeLengthMismatch))
775        );
776    }
777
778    #[test]
779    fn test_rdr_rule_with_to_v4_address_range_to_v6_address_range() {
780        assert_eq!(
781            parse_str_to_rdr_rules(
782                "rdr proto tcp to 1.2.3.4 range 10000:10005 -> to 1234:5678:: range 20000:20005;"
783            ),
784            Err(Error::Invalid(InvalidReason::MixedIPVersions))
785        );
786    }
787
788    #[test]
789    fn test_rdr_rule_with_to_v6_address_range_to_v6_address_range() {
790        assert_eq!(
791            parse_str_to_rdr_rules(
792                "rdr proto tcp to 1234:5678:: range 10000:10005 -> to 2345:6789:: range 20000:20005;"
793            ),
794            Ok(vec![filter::Rdr {
795                proto: filter::SocketProtocol::Tcp,
796                dst_addr: fidl_ip!("1234:5678::"),
797                dst_port_range: filter::PortRange { start: 10000, end: 10005 },
798                new_dst_addr: fidl_ip!("2345:6789::"),
799                new_dst_port_range: filter::PortRange { start: 20000, end: 20005 },
800                nic: 0,
801            }])
802        );
803    }
804}