netfilter/
grammar.rs

1// Copyright 2024 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 fidl_fuchsia_net_filter_ext as filter_ext;
6use pest_derive::Parser;
7
8#[derive(Parser)]
9#[grammar_inline = r#"
10rules = { SOI ~ (rule ~ ";")+ ~ EOI }
11
12rule = {
13     action ~
14     direction ~
15     proto ~
16     devclass ~
17     src ~
18     dst ~
19     log ~
20     state
21}
22
23nat_rules = { SOI ~ (nat ~ ";")+ ~ EOI }
24
25nat = {
26     "nat" ~
27     proto ~
28     "from" ~ subnet ~
29     "->"  ~
30     "from" ~ ipaddr
31}
32
33rdr_rules = { SOI ~ (rdr ~ ";")+ ~ EOI }
34
35rdr = {
36     "rdr" ~
37     proto ~
38     "to" ~ ipaddr ~ port_range ~
39     "->" ~
40     "to" ~ ipaddr ~ port_range
41}
42
43action = { pass | drop | dropreset }
44  pass = { "pass" }
45  drop = { "drop" }
46  dropreset = { "dropreset" }
47
48direction = { incoming | outgoing }
49  incoming = { "in" }
50  outgoing = { "out" }
51
52proto = { ("proto" ~ (tcp | udp | icmp))? }
53  tcp = { "tcp" }
54  udp = { "udp" }
55  icmp = { "icmp" }
56
57devclass = { ("devclass" ~ (virt | ethernet | wlan | ppp | bridge | ap | lowpan))? }
58  virt = { "virt" }
59  ethernet = { "ethernet" }
60  wlan = { "wlan" }
61  ppp = { "ppp" }
62  bridge = { "bridge" }
63  ap = { "ap" }
64  lowpan = { "lowpan" }
65
66src = { ("from" ~ invertible_subnet? ~ port_range?)? }
67dst = { ("to" ~ invertible_subnet? ~ port_range?)? }
68
69invertible_subnet = { not? ~ subnet }
70  not = { "!" }
71
72subnet = ${ ipaddr ~ "/" ~ prefix_len }
73
74ipaddr = { ipv4addr | ipv6addr }
75  ipv4addr = @{ ASCII_DIGIT{1,3} ~ ("." ~ ASCII_DIGIT{1,3}){3} }
76  ipv6addr = @{ (ASCII_HEX_DIGIT | ":")+ }
77
78prefix_len = @{ ASCII_DIGIT+ }
79
80port_range = { port ~ port_num | range ~ port_num ~ ":" ~ port_num }
81  port = { "port" }
82  range = { "range" }
83  port_num = @{ ASCII_DIGIT+ }
84
85log = { ("log")? }
86
87state = { (state_adj ~ "state")? }
88  state_adj = { "keep" | "no" }
89
90WHITESPACE = _{ " " }
91"#]
92
93pub struct FilterRuleParser;
94
95#[derive(Debug, PartialEq)]
96pub enum Error {
97    // Boxing `pest::error::Error<...>` because it's >184 bytes as of writing.
98    Pest(Box<pest::error::Error<Rule>>),
99    Addr(std::net::AddrParseError),
100    Num(std::num::ParseIntError),
101    Invalid(InvalidReason),
102    RoutineNotProvided(crate::parser::Direction),
103    Fidl(filter_ext::FidlConversionError),
104}
105
106#[derive(Debug, PartialEq)]
107pub enum InvalidReason {
108    MixedIPVersions,
109    InvalidPortRange,
110    PortRangeLengthMismatch,
111}
112
113impl std::fmt::Display for InvalidReason {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        match self {
116            Self::MixedIPVersions => write!(f, "mixed IP versions"),
117            Self::InvalidPortRange => write!(f, "invalid port range"),
118            Self::PortRangeLengthMismatch => write!(f, "port range length mismatch"),
119        }
120    }
121}
122
123impl std::fmt::Display for Error {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        match self {
126            Self::Pest(e) => write!(f, "pest error: {}", e),
127            Self::Addr(e) => std::fmt::Display::fmt(e, f),
128            Self::Num(e) => std::fmt::Display::fmt(e, f),
129            Self::Invalid(e) => write!(f, "invalid: {}", e),
130            Self::RoutineNotProvided(e) => write!(f, "expected routine for direction: {:?}", e),
131            Self::Fidl(e) => std::fmt::Display::fmt(e, f),
132        }
133    }
134}
135
136impl std::error::Error for Error {}