blob: 9d149da6e55de07dac4e2b163cd506abad426733 [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fidl_fuchsia_net_filter_ext as filter_ext;
use pest_derive::Parser;
#[derive(Parser)]
#[grammar_inline = r#"
rules = { SOI ~ (rule ~ ";")+ ~ EOI }
rule = {
action ~
direction ~
proto ~
devclass ~
src ~
dst ~
log ~
state
}
nat_rules = { SOI ~ (nat ~ ";")+ ~ EOI }
nat = {
"nat" ~
proto ~
"from" ~ subnet ~
"->" ~
"from" ~ ipaddr
}
rdr_rules = { SOI ~ (rdr ~ ";")+ ~ EOI }
rdr = {
"rdr" ~
proto ~
"to" ~ ipaddr ~ port_range ~
"->" ~
"to" ~ ipaddr ~ port_range
}
action = { pass | drop | dropreset }
pass = { "pass" }
drop = { "drop" }
dropreset = { "dropreset" }
direction = { incoming | outgoing }
incoming = { "in" }
outgoing = { "out" }
proto = { ("proto" ~ (tcp | udp | icmp))? }
tcp = { "tcp" }
udp = { "udp" }
icmp = { "icmp" }
devclass = { ("devclass" ~ (virt | ethernet | wlan | ppp | bridge | ap))? }
virt = { "virt" }
ethernet = { "ethernet" }
wlan = { "wlan" }
ppp = { "ppp" }
bridge = { "bridge" }
ap = { "ap" }
src = { ("from" ~ invertible_subnet? ~ port_range?)? }
dst = { ("to" ~ invertible_subnet? ~ port_range?)? }
invertible_subnet = { not? ~ subnet }
not = { "!" }
subnet = ${ ipaddr ~ "/" ~ prefix_len }
ipaddr = { ipv4addr | ipv6addr }
ipv4addr = @{ ASCII_DIGIT{1,3} ~ ("." ~ ASCII_DIGIT{1,3}){3} }
ipv6addr = @{ (ASCII_HEX_DIGIT | ":")+ }
prefix_len = @{ ASCII_DIGIT+ }
port_range = { port ~ port_num | range ~ port_num ~ ":" ~ port_num }
port = { "port" }
range = { "range" }
port_num = @{ ASCII_DIGIT+ }
log = { ("log")? }
state = { (state_adj ~ "state")? }
state_adj = { "keep" | "no" }
WHITESPACE = _{ " " }
"#]
pub struct FilterRuleParser;
#[derive(Debug, PartialEq)]
pub enum Error {
Pest(pest::error::Error<Rule>),
Addr(std::net::AddrParseError),
Num(std::num::ParseIntError),
Invalid(InvalidReason),
RoutineNotProvided(crate::parser::Direction),
Fidl(filter_ext::FidlConversionError),
}
#[derive(Debug, PartialEq)]
pub enum InvalidReason {
MixedIPVersions,
InvalidPortRange,
PortRangeLengthMismatch,
}
impl std::fmt::Display for InvalidReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MixedIPVersions => write!(f, "mixed IP versions"),
Self::InvalidPortRange => write!(f, "invalid port range"),
Self::PortRangeLengthMismatch => write!(f, "port range length mismatch"),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Pest(e) => write!(f, "pest error: {}", e),
Self::Addr(e) => std::fmt::Display::fmt(e, f),
Self::Num(e) => std::fmt::Display::fmt(e, f),
Self::Invalid(e) => write!(f, "invalid: {}", e),
Self::RoutineNotProvided(e) => write!(f, "expected routine for direction: {:?}", e),
Self::Fidl(e) => std::fmt::Display::fmt(e, f),
}
}
}
impl std::error::Error for Error {}