diff --git a/README.md b/README.md index eb6516e..5bf0084 100644 --- a/README.md +++ b/README.md @@ -59,24 +59,26 @@ Check out the `tests/` directory for more usage examples. This example applies a ruleset that creates and deletes a table to nftables. ```rust -use nft::{batch::Batch, helper, schema, types}; +use nftables::{batch::Batch, helper, schema, types}; /// Applies a ruleset to nftables. fn test_apply_ruleset() { let ruleset = example_ruleset(); - nft::helper::apply_ruleset(&ruleset, None, None).unwrap(); + helper::apply_ruleset(&ruleset, None, None).unwrap(); } -fn example_ruleset() -> schema::Nftables { +fn example_ruleset() -> schema::Nftables<'static> { let mut batch = Batch::new(); - batch.add(schema::NfListObject::Table(schema::Table::new( - types::NfFamily::IP, - "test-table-01".to_string(), - ))); - batch.delete(schema::NfListObject::Table(schema::Table::new( - types::NfFamily::IP, - "test-table-01".to_string(), - ))); + batch.add(schema::NfListObject::Table(schema::Table { + family: types::NfFamily::IP, + name: "test-table-01".into(), + ..Default::default() + })); + batch.delete(schema::NfListObject::Table(schema::Table { + family: types::NfFamily::IP, + name: "test-table-01".into(), + ..Default::default() + })); batch.to_nftables() } ``` @@ -90,16 +92,16 @@ fn test_chain_table_rule_inet() { // nft add table inet some_inet_table // nft add chain inet some_inet_table some_inet_chain '{ type filter hook forward priority 0; policy accept; }' let expected: Nftables = Nftables { - objects: vec![ + objects: Cow::Borrowed(&[ NfObject::CmdObject(NfCmd::Add(NfListObject::Table(Table { family: NfFamily::INet, - name: "some_inet_table".to_string(), + name: Cow::Borrowed("some_inet_table"), handle: None, }))), NfObject::CmdObject(NfCmd::Add(NfListObject::Chain(Chain { family: NfFamily::INet, - table: "some_inet_table".to_string(), - name: "some_inet_chain".to_string(), + table: Cow::Borrowed("some_inet_table"), + name: Cow::Borrowed("some_inet_chain"), newname: None, handle: None, _type: Some(NfChainType::Filter), @@ -108,7 +110,7 @@ fn test_chain_table_rule_inet() { dev: None, policy: Some(NfChainPolicy::Accept), }))), - ], + ]), }; let json = json!({"nftables":[{"add":{"table":{"family":"inet","name":"some_inet_table"}}},{"add":{"chain":{"family":"inet","table":"some_inet_table","name":"some_inet_chain","type":"filter","hook":"forward","policy":"accept"}}}]}); println!("{}", &json); diff --git a/src/batch.rs b/src/batch.rs index b7121ea..935bb8c 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -4,50 +4,52 @@ use crate::schema::{NfCmd, NfListObject, NfObject, Nftables}; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Batch manages nftables objects and is used to prepare an nftables payload. -pub struct Batch { - data: Vec, +pub struct Batch<'a> { + data: Vec>, } -impl Default for Batch { +impl Default for Batch<'_> { fn default() -> Self { Self::new() } } -impl Batch { +impl<'a> Batch<'a> { /// Creates an empty Batch instance. - pub fn new() -> Batch { + pub fn new() -> Batch<'a> { Batch { data: Vec::new() } } /// Adds object with `add` command to Batch. - pub fn add(&mut self, obj: NfListObject) { + pub fn add(&mut self, obj: NfListObject<'a>) { self.data.push(NfObject::CmdObject(NfCmd::Add(obj))) } /// Adds object with `delete` command to Batch. - pub fn delete(&mut self, obj: NfListObject) { + pub fn delete(&mut self, obj: NfListObject<'a>) { self.data.push(NfObject::CmdObject(NfCmd::Delete(obj))) } /// Adds a command to Batch. - pub fn add_cmd(&mut self, cmd: NfCmd) { + pub fn add_cmd(&mut self, cmd: NfCmd<'a>) { self.data.push(NfObject::CmdObject(cmd)) } /// Adds a list object (without a command) directly to Batch. /// This corresponds to the descriptive output format of `nft -j list ruleset`. - pub fn add_obj(&mut self, obj: NfListObject) { - self.data.push(NfObject::ListObject(Box::new(obj))) + pub fn add_obj(&mut self, obj: NfListObject<'a>) { + self.data.push(NfObject::ListObject(obj)) } /// Adds all given objects to the batch. - pub fn add_all(&mut self, objs: Vec) { + pub fn add_all>>(&mut self, objs: I) { self.data.extend(objs) } /// Wraps Batch in nftables object. - pub fn to_nftables(self) -> Nftables { - Nftables { objects: self.data } + pub fn to_nftables(self) -> Nftables<'a> { + Nftables { + objects: self.data.into(), + } } } diff --git a/src/expr.rs b/src/expr.rs index e3e5a5c..9e343dc 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use std::collections::HashSet; +use std::{borrow::Cow, collections::HashSet}; use crate::stmt::{Counter, JumpTarget, Statement}; @@ -7,78 +7,78 @@ use crate::stmt::{Counter, JumpTarget, Statement}; #[serde(untagged)] /// Expressions are the building blocks of (most) statements. /// In their most basic form, they are just immediate values represented as a JSON string, integer or boolean type. -pub enum Expression { +pub enum Expression<'a> { // immediates - String(String), + String(Cow<'a, str>), Number(u32), Boolean(bool), /// List expressions are constructed by plain arrays containing of an arbitrary number of expressions. - List(Vec), - BinaryOperation(BinaryOperation), - Range(Range), + List(Vec>), + BinaryOperation(Box>), + Range(Box>), - Named(NamedExpression), - Verdict(Verdict), + Named(NamedExpression<'a>), + Verdict(Verdict<'a>), } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] /// Wrapper for non-immediate `Expression`s. -pub enum NamedExpression { +pub enum NamedExpression<'a> { /// Concatenate several expressions. - Concat(Vec), + Concat(Vec>), /// This object constructs an anonymous set. /// For mappings, an array of arrays with exactly two elements is expected. - Set(Vec), - Map(Box), - Prefix(Prefix), + Set(Vec>), + Map(Box>), + Prefix(Prefix<'a>), - Payload(Payload), + Payload(Payload<'a>), - Exthdr(Exthdr), + Exthdr(Exthdr<'a>), #[serde(rename = "tcp option")] - TcpOption(TcpOption), + TcpOption(TcpOption<'a>), #[serde(rename = "sctp chunk")] - SctpChunk(SctpChunk), + SctpChunk(SctpChunk<'a>), Meta(Meta), RT(RT), - CT(CT), + CT(CT<'a>), Numgen(Numgen), - JHash(JHash), + JHash(JHash<'a>), SymHash(SymHash), Fib(Fib), - Elem(Elem), - Socket(Socket), - Osf(Osf), + Elem(Elem<'a>), + Socket(Socket<'a>), + Osf(Osf<'a>), } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "map")] /// Map a key to a value. -pub struct Map { +pub struct Map<'a> { /// Map key. - pub key: Expression, + pub key: Expression<'a>, /// Mapping expression consisting of value/target pairs. - pub data: Expression, + pub data: Expression<'a>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(untagged)] /// Item in an anonymous set. -pub enum SetItem { +pub enum SetItem<'a> { /// A set item containing a single expression. - Element(Expression), + Element(Expression<'a>), /// A set item mapping two expressions. - Mapping(Expression, Expression), + Mapping(Expression<'a>, Expression<'a>), /// A set item mapping an expression to a statement. - MappingStatement(Expression, Statement), + MappingStatement(Expression<'a>, Statement<'a>), } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "prefix")] /// Construct an IPv4 or IPv6 prefix consisting of address part in `addr` and prefix length in `len`. -pub struct Prefix { - pub addr: Box, +pub struct Prefix<'a> { + pub addr: Box>, pub len: u32, } @@ -86,14 +86,14 @@ pub struct Prefix { #[serde(rename = "range")] /// Construct a range of values. /// The first array item denotes the lower boundary, the second one the upper boundary. -pub struct Range { - pub range: Vec, +pub struct Range<'a> { + pub range: [Expression<'a>; 2], } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(untagged)] -pub enum Payload { - PayloadField(PayloadField), +pub enum Payload<'a> { + PayloadField(PayloadField<'a>), PayloadRaw(PayloadRaw), } @@ -110,9 +110,9 @@ pub struct PayloadRaw { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Construct a payload expression, i.e. a reference to a certain part of packet data. /// Allows to reference a field by name (`field`) in a named packet header (`protocol`). -pub struct PayloadField { - pub protocol: String, - pub field: String, +pub struct PayloadField<'a> { + pub protocol: Cow<'a, str>, + pub field: Cow<'a, str>, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] @@ -133,26 +133,26 @@ pub enum PayloadBase { #[serde(rename = "exthdr")] /// Create a reference to a field (field) in an IPv6 extension header (name). /// `offset` is used only for rt0 protocol. -pub struct Exthdr { - pub name: String, - pub field: String, +pub struct Exthdr<'a> { + pub name: Cow<'a, str>, + pub field: Cow<'a, str>, pub offset: u32, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "tcp option")] /// Create a reference to a field (`field`) of a TCP option header (`name`). -pub struct TcpOption { - pub name: String, - pub field: String, +pub struct TcpOption<'a> { + pub name: Cow<'a, str>, + pub field: Cow<'a, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "sctp chunk")] /// Create a reference to a field (`field`) of an SCTP chunk (`name`). -pub struct SctpChunk { - pub name: String, - pub field: String, +pub struct SctpChunk<'a> { + pub name: Cow<'a, str>, + pub field: Cow<'a, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -224,8 +224,8 @@ pub enum RTFamily { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "ct")] /// Create a reference to packet conntrack data. -pub struct CT { - pub key: String, +pub struct CT<'a> { + pub key: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] pub family: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -270,12 +270,12 @@ pub enum NgMode { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "jhash")] /// Hash packet data -pub struct JHash { +pub struct JHash<'a> { #[serde(rename = "mod")] pub hash_mod: u32, #[serde(skip_serializing_if = "Option::is_none")] pub offset: Option, - pub expr: Box, + pub expr: Box>, #[serde(skip_serializing_if = "Option::is_none")] pub seed: Option, } @@ -324,67 +324,67 @@ pub enum FibFlag { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Represents a binary operation to be used in an `Expression`. -pub enum BinaryOperation { +pub enum BinaryOperation<'a> { #[serde(rename = "&")] /// Binary AND (`&`) - AND(Box, Box), + AND(Expression<'a>, Expression<'a>), #[serde(rename = "|")] /// Binary OR (`|`) - OR(Box, Box), + OR(Expression<'a>, Expression<'a>), #[serde(rename = "^")] /// Binary XOR (`^`) - XOR(Box, Box), + XOR(Expression<'a>, Expression<'a>), #[serde(rename = "<<")] /// Left shift (`<<`) - LSHIFT(Box, Box), + LSHIFT(Expression<'a>, Expression<'a>), #[serde(rename = ">>")] /// Right shift (`>>`) - RSHIFT(Box, Box), + RSHIFT(Expression<'a>, Expression<'a>), } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] /// Verdict expression. -pub enum Verdict { +pub enum Verdict<'a> { Accept, Drop, Continue, Return, - Jump(JumpTarget), - Goto(JumpTarget), + Jump(JumpTarget<'a>), + Goto(JumpTarget<'a>), } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "elem")] /// Explicitly set element object. -pub struct Elem { - pub val: Box, +pub struct Elem<'a> { + pub val: Box>, pub timeout: Option, pub expires: Option, - pub comment: Option, - pub counter: Option, + pub comment: Option>, + pub counter: Option>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "socket")] /// Construct a reference to packet’s socket. -pub struct Socket { - pub key: String, +pub struct Socket<'a> { + pub key: Cow<'a, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "osf")] /// Perform OS fingerprinting. /// This expression is typically used in the LHS of a `match` statement. -pub struct Osf { +pub struct Osf<'a> { /// Name of the OS signature to match. /// All signatures can be found at pf.os file. /// Use "unknown" for OS signatures that the expression could not detect. - pub key: String, + pub key: Cow<'a, str>, /// Do TTL checks on the packet to determine the operating system. pub ttl: OsfTtl, } diff --git a/src/helper.rs b/src/helper.rs index 1f4de22..c996eb7 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -32,20 +32,20 @@ pub enum NftablesError { pub fn get_current_ruleset( program: Option<&str>, - args: Option>, -) -> Result { + args: Option<&[&str]>, +) -> Result, NftablesError> { let output = get_current_ruleset_raw(program, args)?; serde_json::from_str(&output).map_err(NftablesError::NftInvalidJson) } pub fn get_current_ruleset_raw( program: Option<&str>, - args: Option>, + args: Option<&[&str]>, ) -> Result { let mut nft_cmd = get_command(program); let default_args = ["list", "ruleset"]; - let args = match &args { - Some(args) => args.as_slice(), + let args = match args { + Some(args) => args, None => &default_args, }; let program = nft_cmd.get_program().to_str().unwrap().to_string(); @@ -77,29 +77,23 @@ pub fn get_current_ruleset_raw( pub fn apply_ruleset( nftables: &Nftables, program: Option<&str>, - args: Option>, + args: Option<&[&str]>, ) -> Result<(), NftablesError> { let nftables = serde_json::to_string(nftables).expect("failed to serialize Nftables struct"); - apply_ruleset_raw(nftables, program, args) + apply_ruleset_raw(&nftables, program, args) } pub fn apply_ruleset_raw( - payload: String, + payload: &str, program: Option<&str>, - args: Option>, + args: Option<&[&str]>, ) -> Result<(), NftablesError> { let mut nft_cmd = get_command(program); let default_args = ["-j", "-f", "-"]; - let args: Vec<&str> = match args { - Some(mut args) => { - args.extend_from_slice(&default_args); - args - } - None => default_args.to_vec(), - }; let program = nft_cmd.get_program().to_str().unwrap().to_string(); let mut process = nft_cmd - .args(args) + .args(args.into_iter().flatten()) + .args(default_args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() diff --git a/src/schema.rs b/src/schema.rs index b389177..b1e9cf9 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{borrow::Cow, collections::HashSet}; use crate::{ expr::Expression, stmt::Statement, types::*, visitor::single_string_to_option_vec, @@ -15,59 +15,59 @@ use strum_macros::EnumString; /// See [libnftables-json global structure](Global Structure). /// /// (Global Structure): -pub struct Nftables { +pub struct Nftables<'a> { /// An array containing [commands](NfCmd) (for input) or [ruleset elements](NfListObject) (for output). #[serde(rename = "nftables")] - pub objects: Vec, + pub objects: Cow<'a, [NfObject<'a>]>, } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(untagged)] /// A [ruleset element](NfListObject) or [command](NfCmd) in an [nftables document](Nftables). -pub enum NfObject { +pub enum NfObject<'a> { /// A command. - CmdObject(NfCmd), + CmdObject(NfCmd<'a>), /// A ruleset element. - ListObject(Box), + ListObject(NfListObject<'a>), } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] /// A ruleset element in an [nftables document](Nftables). -pub enum NfListObject { +pub enum NfListObject<'a> { /// A table element. - Table(Table), + Table(Table<'a>), /// A chain element. - Chain(Chain), + Chain(Chain<'a>), /// A rule element. - Rule(Rule), + Rule(Rule<'a>), /// A set element. - Set(Set), + Set(Box>), /// A map element. - Map(Map), + Map(Box>), /// An element manipulation. - Element(Element), + Element(Element<'a>), /// A flow table. - FlowTable(FlowTable), + FlowTable(FlowTable<'a>), /// A counter. - Counter(Counter), + Counter(Counter<'a>), /// A quota. - Quota(Quota), + Quota(Quota<'a>), #[serde(rename = "ct helper")] /// A conntrack helper (ct helper). - CTHelper(CTHelper), + CTHelper(CTHelper<'a>), /// A limit. - Limit(Limit), + Limit(Limit<'a>), #[serde(rename = "metainfo")] /// The metainfo object. - MetainfoObject(MetainfoObject), + MetainfoObject(MetainfoObject<'a>), /// A conntrack timeout (ct timeout). - CTTimeout(CTTimeout), + CTTimeout(CTTimeout<'a>), #[serde(rename = "ct expectation")] /// A conntrack expectation (ct expectation). - CTExpectation(CTExpectation), + CTExpectation(CTExpectation<'a>), /// A synproxy object. - SynProxy(SynProxy), + SynProxy(SynProxy<'a>), } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -77,74 +77,74 @@ pub enum NfListObject { /// Its value is a ruleset element - basically identical to output elements, /// apart from certain properties which may be interpreted differently or are /// required when output generally omits them. -pub enum NfCmd { +pub enum NfCmd<'a> { /// Add a new ruleset element to the kernel. - Add(NfListObject), + Add(NfListObject<'a>), /// Replace a rule. /// /// In [RULE](Rule), the **handle** property is mandatory and identifies /// the rule to be replaced. - Replace(Rule), + Replace(Rule<'a>), /// Identical to [add command](NfCmd::Add), but returns an error if the object already exists. - Create(NfListObject), // TODO: ADD_OBJECT is subset of NfListObject + Create(NfListObject<'a>), // TODO: ADD_OBJECT is subset of NfListObject /// Insert an object. /// /// This command is identical to [add](NfCmd::Add) for rules, but instead of /// appending the rule to the chain by default, it inserts at first position. /// If a handle or index property is given, the rule is inserted before the /// rule identified by those properties. - Insert(NfListObject), + Insert(NfListObject<'a>), /// Delete an object from the ruleset. /// /// Only the minimal number of properties required to uniquely identify an /// object is generally needed in the enclosed object. /// For most ruleset elements, this is **family** and **table** plus either /// **handle** or **name** (except rules since they don’t have a name). - Delete(NfListObject), // TODO: ADD_OBJECT is subset of NfListObject + Delete(NfListObject<'a>), // TODO: ADD_OBJECT is subset of NfListObject /// List ruleset elements. /// /// The plural forms are used to list all objects of that kind, /// optionally filtered by family and for some, also table. - List(NfListObject), + List(NfListObject<'a>), /// Reset state in suitable objects, i.e. zero their internal counter. - Reset(ResetObject), + Reset(ResetObject<'a>), /// Empty contents in given object, e.g. remove all chains from given table /// or remove all elements from given set. - Flush(FlushObject), + Flush(FlushObject<'a>), /// Rename a [chain](Chain). /// /// The new name is expected in a dedicated property named **newname**. - Rename(Chain), + Rename(Chain<'a>), } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] /// Reset state in suitable objects, i.e. zero their internal counter. -pub enum ResetObject { +pub enum ResetObject<'a> { /// A counter to reset. - Counter(Counter), + Counter(Counter<'a>), /// A list of counters to reset. - Counters(Vec), + Counters(Cow<'a, [Counter<'a>]>), /// A quota to reset. - Quota(Quota), + Quota(Quota<'a>), /// A list of quotas to reset. - Quotas(Vec), + Quotas(Cow<'a, [Quota<'a>]>), } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] /// Empty contents in given object, e.g. remove all chains from given table or remove all elements from given set. -pub enum FlushObject { +pub enum FlushObject<'a> { /// A table to flush (i.e., remove all chains from table). - Table(Table), + Table(Table<'a>), /// A chain to flush (i.e., remove all rules from chain). - Chain(Chain), + Chain(Chain<'a>), /// A set to flush (i.e., remove all elements from set). - Set(Set), + Set(Box>), /// A map to flush (i.e., remove all elements from map). - Map(Map), + Map(Box>), /// A meter to flush. - Meter(Meter), + Meter(Meter<'a>), /// Flush the live ruleset (i.e., remove all elements from live ruleset). Ruleset(Option), } @@ -153,11 +153,11 @@ pub enum FlushObject { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] /// This object describes a table. -pub struct Table { +pub struct Table<'a> { /// The table’s [family](NfFamily), e.g. "ip" or "ip6". pub family: NfFamily, /// The table’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The table’s handle. /// @@ -167,11 +167,11 @@ pub struct Table { } /// Default table. -impl Default for Table { +impl Default for Table<'_> { fn default() -> Self { Table { family: DEFAULT_FAMILY, - name: DEFAULT_TABLE.to_string(), + name: DEFAULT_TABLE.into(), handle: None, } } @@ -179,16 +179,16 @@ impl Default for Table { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] /// This object describes a chain. -pub struct Chain { +pub struct Chain<'a> { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'a, str>, /// The chain’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// New name of the chain when supplied to the [rename command](NfCmd::Rename). - pub newname: Option, + pub newname: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// The chain’s handle. /// In input, it is used only in [delete command](NfCmd::Delete) as alternative to **name**. @@ -216,7 +216,7 @@ pub struct Chain { /// Required for [base chains](Base chains). /// /// (Base chains): - pub dev: Option, + pub dev: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// The chain’s [policy](NfChainPolicy). /// Required for [base chains](Base chains). @@ -226,12 +226,12 @@ pub struct Chain { } /// Default Chain. -impl Default for Chain { +impl Default for Chain<'_> { fn default() -> Self { Chain { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: DEFAULT_CHAIN.to_string(), + table: DEFAULT_TABLE.into(), + name: DEFAULT_CHAIN.into(), newname: None, handle: None, _type: None, @@ -248,17 +248,17 @@ impl Default for Chain { /// /// Basic building blocks of rules are statements. /// Each rule consists of at least one. -pub struct Rule { +pub struct Rule<'a> { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'a, str>, /// The chain’s name. - pub chain: String, + pub chain: Cow<'a, str>, /// An array of statements this rule consists of. /// /// In input, it is used in [add](NfCmd::Add)/[insert](NfCmd::Insert)/[replace](NfCmd::Replace) commands only. - pub expr: Vec, + pub expr: Cow<'a, [Statement<'a>]>, #[serde(skip_serializing_if = "Option::is_none")] /// The rule’s handle. /// @@ -272,17 +272,17 @@ pub struct Rule { pub index: Option, #[serde(skip_serializing_if = "Option::is_none")] /// Optional rule comment. - pub comment: Option, + pub comment: Option>, } /// Default rule with no expressions. -impl Default for Rule { +impl Default for Rule<'_> { fn default() -> Self { Rule { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - chain: DEFAULT_CHAIN.to_string(), - expr: vec![], + table: DEFAULT_TABLE.into(), + chain: DEFAULT_CHAIN.into(), + expr: [][..].into(), handle: None, index: None, comment: None, @@ -292,13 +292,13 @@ impl Default for Rule { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] /// Named set that holds expression elements. -pub struct Set { +pub struct Set<'a> { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'a, str>, /// The set’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The set’s handle. For input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, @@ -306,7 +306,7 @@ pub struct Set { /// The set’s datatype. /// /// The set type might be a string, such as `"ipv4_addr"` or an array consisting of strings (for concatenated types). - pub set_type: SetTypeValue, + pub set_type: SetTypeValue<'a>, #[serde(skip_serializing_if = "Option::is_none")] /// The set’s policy. pub policy: Option, @@ -318,7 +318,7 @@ pub struct Set { /// /// A single set element might be given as string, integer or boolean value for simple cases. If additional properties are required, a formal elem object may be used. /// Multiple elements may be given in an array. - pub elem: Option>, + pub elem: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] /// Element timeout in seconds. pub timeout: Option, @@ -332,16 +332,16 @@ pub struct Set { /// Optional set comment. /// /// Set comment attribute requires at least nftables 0.9.7 and kernel 5.10 - pub comment: Option, + pub comment: Option>, } /// Default set `"myset"` with type `ipv4_addr`. -impl Default for Set { +impl Default for Set<'_> { fn default() -> Self { Set { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "myset".to_string(), + table: DEFAULT_TABLE.into(), + name: "myset".into(), handle: None, set_type: SetTypeValue::Single(SetType::Ipv4Addr), policy: None, @@ -358,13 +358,13 @@ impl Default for Set { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] /// Named map that holds expression elements. /// Maps are a special form of sets in that they translate a unique key to a value. -pub struct Map { +pub struct Map<'a> { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'a, str>, /// The map’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The map’s handle. For input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, @@ -373,9 +373,9 @@ pub struct Map { /// /// The set type might be a string, such as `"ipv4_addr"`` or an array /// consisting of strings (for concatenated types). - pub set_type: SetTypeValue, + pub set_type: SetTypeValue<'a>, /// Type of values this set maps to (i.e. this set is a map). - pub map: SetTypeValue, + pub map: SetTypeValue<'a>, #[serde(skip_serializing_if = "Option::is_none")] /// The map’s policy. pub policy: Option, @@ -387,7 +387,7 @@ pub struct Map { /// /// A single set element might be given as string, integer or boolean value for simple cases. If additional properties are required, a formal elem object may be used. /// Multiple elements may be given in an array. - pub elem: Option>, + pub elem: Option]>>, #[serde(skip_serializing_if = "Option::is_none")] /// Element timeout in seconds. pub timeout: Option, @@ -401,16 +401,16 @@ pub struct Map { /// Optional map comment. /// /// The map/set comment attribute requires at least nftables 0.9.7 and kernel 5.10 - pub comment: Option, + pub comment: Option>, } /// Default map "mymap" that maps ipv4addrs. -impl Default for Map { +impl Default for Map<'_> { fn default() -> Self { Map { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "mymap".to_string(), + table: DEFAULT_TABLE.into(), + name: "mymap".into(), handle: None, set_type: SetTypeValue::Single(SetType::Ipv4Addr), map: SetTypeValue::Single(SetType::Ipv4Addr), @@ -429,11 +429,11 @@ impl Default for Map { #[serde(untagged)] /// Wrapper for single or concatenated set types. /// The set type might be a string, such as `"ipv4_addr"` or an array consisting of strings (for concatenated types). -pub enum SetTypeValue { +pub enum SetTypeValue<'a> { /// Single set type. Single(SetType), /// Concatenated set types. - Concatenated(Vec), + Concatenated(Cow<'a, [SetType]>), } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, EnumString)] @@ -507,27 +507,27 @@ pub enum SetOp { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] /// Manipulate element(s) in a named set. -pub struct Element { +pub struct Element<'a> { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'a, str>, /// The set’s name. - pub name: String, + pub name: Cow<'a, str>, /// A single set element might be given as string, integer or boolean value for simple cases. /// If additional properties are required, a formal `elem` object may be used. /// Multiple elements may be given in an array. - pub elem: Vec, + pub elem: Cow<'a, [Expression<'a>]>, } /// Default manipulation element for [set](Set) "myset". -impl Default for Element { +impl Default for Element<'_> { fn default() -> Self { Element { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "myset".to_string(), - elem: Vec::new(), + table: DEFAULT_TABLE.into(), + name: "myset".into(), + elem: [][..].into(), } } } @@ -537,13 +537,13 @@ impl Default for Element { /// by using a conntrack-based network stack bypass. /// /// [Flowtables]: https://wiki.nftables.org/wiki-nftables/index.php/Flowtables -pub struct FlowTable { +pub struct FlowTable<'a> { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'a, str>, /// The flow table’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The flow table’s handle. In input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, @@ -560,17 +560,17 @@ pub struct FlowTable { /// The *devices* are specified as iifname(s) of the input interface(s) of the traffic that should be offloaded. /// /// Devices are required for both traffic directions. - /// Vec of device names, e.g. `vec!["wg0".to_string(), "wg0".to_string()]`. - pub dev: Option>, + /// Cow slice of device names, e.g. `vec!["wg0".into(), "wg1".into()].into()`. + pub dev: Option]>>, } /// Default [flowtable](FlowTable) named "myflowtable". -impl Default for FlowTable { +impl Default for FlowTable<'_> { fn default() -> Self { FlowTable { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "myflowtable".to_string(), + table: DEFAULT_TABLE.into(), + name: "myflowtable".into(), handle: None, hook: None, prio: None, @@ -586,13 +586,13 @@ impl Default for FlowTable { /// With nftables you need to explicitly specify a counter for each rule you want to count. /// /// [counter]: https://wiki.nftables.org/wiki-nftables/index.php/Counters -pub struct Counter { +pub struct Counter<'a> { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'a, str>, /// The counter’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The counter’s handle. In input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, @@ -604,12 +604,12 @@ pub struct Counter { } /// Default [counter](Counter) named "mycounter". -impl Default for Counter { +impl Default for Counter<'_> { fn default() -> Self { Counter { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "mycounter".to_string(), + table: DEFAULT_TABLE.into(), + name: "mycounter".into(), handle: None, packets: None, bytes: None, @@ -629,13 +629,13 @@ impl Default for Counter { /// * only after the byte count is over the threshold. /// /// (Quota): -pub struct Quota { +pub struct Quota<'a> { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'a, str>, /// The quota’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The quota’s handle. In input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, @@ -651,12 +651,12 @@ pub struct Quota { } /// Default [quota](Quota) named "myquota". -impl Default for Quota { +impl Default for Quota<'_> { fn default() -> Self { Quota { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "myquota".to_string(), + table: DEFAULT_TABLE.into(), + name: "myquota".into(), handle: None, bytes: None, used: None, @@ -670,36 +670,36 @@ impl Default for Quota { /// Enable the specified [conntrack helper][Conntrack helpers] for this packet. /// /// [Conntrack helpers]: -pub struct CTHelper { +pub struct CTHelper<'a> { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'a, str>, /// The ct helper’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The ct helper’s handle. In input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, #[serde(rename = "type")] /// The ct helper type name, e.g. "ftp" or "tftp". - pub _type: String, + pub _type: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The ct helper’s layer 4 protocol. - pub protocol: Option, + pub protocol: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// The ct helper’s layer 3 protocol, e.g. "ip" or "ip6". - pub l3proto: Option, + pub l3proto: Option>, } /// Default ftp [ct helper](CTHelper) named "mycthelper". -impl Default for CTHelper { +impl Default for CTHelper<'_> { fn default() -> Self { CTHelper { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "mycthelper".to_string(), + table: DEFAULT_TABLE.into(), + name: "mycthelper".into(), handle: None, - _type: "ftp".to_string(), + _type: "ftp".into(), protocol: None, l3proto: None, } @@ -715,13 +715,13 @@ impl Default for CTHelper { /// /// (Limit): /// (Token bucket): -pub struct Limit { +pub struct Limit<'a> { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'a, str>, /// The limit’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The limit’s handle. In input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, @@ -744,12 +744,12 @@ pub struct Limit { } /// Default [limit](Limit) named "mylimit". -impl Default for Limit { +impl Default for Limit<'_> { fn default() -> Self { Limit { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "mylimit".to_string(), + table: DEFAULT_TABLE.into(), + name: "mylimit".into(), handle: None, rate: None, per: None, @@ -771,10 +771,10 @@ pub enum LimitUnit { } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct Meter { - pub name: String, - pub key: Expression, - pub stmt: Statement, +pub struct Meter<'a> { + pub name: Cow<'a, str>, + pub key: Expression<'a>, + pub stmt: Box>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -792,13 +792,13 @@ impl Default for Ruleset { /// Library information in output. /// /// In output, the first object in an nftables array is a special one containing library information. -pub struct MetainfoObject { +pub struct MetainfoObject<'a> { #[serde(skip_serializing_if = "Option::is_none")] /// The value of version property is equal to the package version as printed by `nft -v`. - pub version: Option, + pub version: Option>, /// The value of release_name property is equal to the release name as printed by `nft -v`. #[serde(skip_serializing_if = "Option::is_none")] - pub release_name: Option, + pub release_name: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// The JSON Schema version. /// @@ -811,7 +811,7 @@ pub struct MetainfoObject { } /// Default (empty) [metainfo object](MetainfoObject). -impl Default for MetainfoObject { +impl Default for MetainfoObject<'_> { fn default() -> Self { MetainfoObject { version: None, @@ -827,13 +827,13 @@ impl Default for MetainfoObject { /// You can use a ct timeout object to specify a connection tracking timeout policy for a particular flow. /// /// [Ct timeout]: -pub struct CTTimeout { +pub struct CTTimeout<'a> { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'a, str>, /// The ct timeout object’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The ct timeout object’s handle. In input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, @@ -842,22 +842,22 @@ pub struct CTTimeout { pub protocol: Option, #[serde(skip_serializing_if = "Option::is_none")] /// The connection state name, e.g. "established", "syn_sent", "close" or "close_wait", for which the timeout value has to be updated. - pub state: Option, + pub state: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// The updated timeout value for the specified connection state. pub value: Option, #[serde(skip_serializing_if = "Option::is_none")] /// The ct timeout object’s layer 3 protocol, e.g. "ip" or "ip6". - pub l3proto: Option, + pub l3proto: Option>, } /// Default [ct timeout](CTTimeout) named "mycttimeout" -impl Default for CTTimeout { +impl Default for CTTimeout<'_> { fn default() -> Self { CTTimeout { family: DEFAULT_FAMILY, - table: DEFAULT_TABLE.to_string(), - name: "mycttimeout".to_string(), + table: DEFAULT_TABLE.into(), + name: "mycttimeout".into(), handle: None, protocol: None, state: None, @@ -871,19 +871,19 @@ impl Default for CTTimeout { /// This object represents a named [conntrack expectation][Ct expectation]. /// /// [Ct expectation]: -pub struct CTExpectation { +pub struct CTExpectation<'a> { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'a, str>, /// The ct expectation object’s name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The ct expectation object’s handle. In input, it is used by delete command only. pub handle: Option, #[serde(skip_serializing_if = "Option::is_none")] /// The ct expectation object’s layer 3 protocol, e.g. "ip" or "ip6". - pub l3proto: Option, + pub l3proto: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// The ct expectation object’s layer 4 protocol. pub protocol: Option, @@ -905,13 +905,13 @@ pub struct CTExpectation { /// /// [SynProxy]: https://wiki.nftables.org/wiki-nftables/index.php/Synproxy #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct SynProxy { +pub struct SynProxy<'a> { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'a, str>, /// The synproxy's name. - pub name: String, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The synproxy's handle. For input, it is used by the [delete command](NfCmd::Delete) only. pub handle: Option, diff --git a/src/stmt.rs b/src/stmt.rs index ffc5243..c52dcaf 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -8,6 +8,7 @@ use crate::types::{RejectCode, SynProxyFlag}; use crate::visitor::single_string_to_option_hashset_logflag; use crate::expr::Expression; +use std::borrow::Cow; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] @@ -15,7 +16,7 @@ use crate::expr::Expression; /// Statements are the building blocks for rules. Each rule consists of at least one. /// /// See . -pub enum Statement { +pub enum Statement<'a> { /// `accept` verdict. Accept(Option), /// `drop` verdict. @@ -25,55 +26,55 @@ pub enum Statement { /// `return` verdict. Return(Option), /// `jump` verdict. Expects a target chain name. - Jump(JumpTarget), + Jump(JumpTarget<'a>), /// `goto` verdict. Expects a target chain name. - Goto(JumpTarget), + Goto(JumpTarget<'a>), - Match(Match), + Match(Match<'a>), /// anonymous or named counter. - Counter(Counter), - Mangle(Mangle), + Counter(Counter<'a>), + Mangle(Mangle<'a>), /// anonymous or named quota. - Quota(QuotaOrQuotaRef), + Quota(QuotaOrQuotaRef<'a>), // TODO: last - Limit(Limit), + Limit(Limit<'a>), /// The Flow statement offloads matching network traffic to flowtables, /// enabling faster forwarding by bypassing standard processing. - Flow(Flow), - FWD(Option), + Flow(Flow<'a>), + FWD(Option>), /// Disable connection tracking for the packet. Notrack, - Dup(Dup), - SNAT(Option), - DNAT(Option), - Masquerade(Option), // masquerade is subset of NAT options - Redirect(Option), // redirect is subset of NAT options + Dup(Dup<'a>), + SNAT(Option>), + DNAT(Option>), + Masquerade(Option>), // masquerade is subset of NAT options + Redirect(Option>), // redirect is subset of NAT options Reject(Option), - Set(Set), + Set(Set<'a>), // TODO: map - Log(Option), + Log(Option>), #[serde(rename = "ct helper")] /// Enable the specified conntrack helper for this packet. - CTHelper(String), // CT helper reference. + CTHelper(Cow<'a, str>), // CT helper reference. - Meter(Meter), - Queue(Queue), + Meter(Meter<'a>), + Queue(Queue<'a>), #[serde(rename = "vmap")] // TODO: vmap is expr, not stmt! - VerdictMap(VerdictMap), + VerdictMap(VerdictMap<'a>), #[serde(rename = "ct count")] - CTCount(CTCount), + CTCount(CTCount<'a>), #[serde(rename = "ct timeout")] /// Assign connection tracking timeout policy. - CTTimeout(Expression), // CT timeout reference. + CTTimeout(Expression<'a>), // CT timeout reference. #[serde(rename = "ct expectation")] /// Assign connection tracking expectation. - CTExpectation(Expression), // CT expectation reference. + CTExpectation(Expression<'a>), // CT expectation reference. /// This represents an xt statement from xtables compat interface. /// Sadly, at this point, it is not possible to provide any further information about its content. @@ -81,7 +82,7 @@ pub enum Statement { /// A netfilter synproxy intercepts new TCP connections and handles the initial 3-way handshake using syncookies instead of conntrack to establish the connection. SynProxy(SynProxy), /// Redirects the packet to a local socket without changing the packet header in any way. - TProxy(TProxy), + TProxy(TProxy<'a>), // TODO: reset // TODO: secmark } @@ -103,8 +104,8 @@ pub struct Continue {} pub struct Return {} #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct JumpTarget { - pub target: String, +pub struct JumpTarget<'a> { + pub target: Cow<'a, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -112,11 +113,11 @@ pub struct JumpTarget { /// /// If the statement evaluates to true, the next statement in this rule is considered. /// If not, processing continues with the next rule in the same chain. -pub struct Match { +pub struct Match<'a> { /// Left hand side of this match. - pub left: Expression, + pub left: Expression<'a>, /// Right hand side of this match. - pub right: Expression, + pub right: Expression<'a>, /// Operator indicating the type of comparison. pub op: Operator, } @@ -124,9 +125,9 @@ pub struct Match { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(untagged)] /// Anonymous or named Counter. -pub enum Counter { +pub enum Counter<'a> { /// A counter referenced by name. - Named(String), + Named(Cow<'a, str>), /// An anonymous counter. Anonymous(Option), } @@ -146,36 +147,36 @@ pub struct AnonymousCounter { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// This changes the packet data or meta info. -pub struct Mangle { +pub struct Mangle<'a> { /// The packet data to be changed, given as an `exthdr`, `payload`, `meta`, `ct` or `ct helper` expression. - pub key: Expression, + pub key: Expression<'a>, /// Value to change data to. - pub value: Expression, + pub value: Expression<'a>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(untagged)] /// Represents an anonymous or named quota object. -pub enum QuotaOrQuotaRef { +pub enum QuotaOrQuotaRef<'a> { /// Anonymous quota object. - Quota(Quota), + Quota(Quota<'a>), /// Reference to a named quota object. - QuotaRef(String), + QuotaRef(Cow<'a, str>), } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Creates an anonymous quota which lives in the rule it appears in. -pub struct Quota { +pub struct Quota<'a> { /// Quota value. pub val: u32, /// Unit of `val`, e.g. `"kbytes"` or `"mbytes"`. If omitted, defaults to `"bytes"`. - pub val_unit: String, + pub val_unit: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// Quota used so far. Optional on input. If given, serves as initial value. pub used: Option, #[serde(skip_serializing_if = "Option::is_none")] /// Unit of `used`. Defaults to `"bytes"`. - pub used_unit: Option, + pub used_unit: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// If `true`, will match if quota was exceeded. Defaults to `false`. pub inv: Option, @@ -183,21 +184,21 @@ pub struct Quota { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Creates an anonymous limit which lives in the rule it appears in. -pub struct Limit { +pub struct Limit<'a> { /// Rate value to limit to. pub rate: u32, #[serde(skip_serializing_if = "Option::is_none")] /// Unit of `rate`, e.g. `"packets"` or `"mbytes"`. If omitted, defaults to `"packets"`. - pub rate_unit: Option, + pub rate_unit: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// Denominator of rate, e.g. "week" or "minutes". - pub per: Option, + pub per: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// Burst value. Defaults to `0`. pub burst: Option, #[serde(skip_serializing_if = "Option::is_none")] /// Unit of `burst`, ignored if `rate_unit` is `"packets"`. Defaults to `"bytes"`. - pub burst_unit: Option, + pub burst_unit: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// If `true`, will match if the limit was exceeded. Defaults to `false`. pub inv: Option, @@ -205,25 +206,25 @@ pub struct Limit { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Forward a packet to a different destination. -pub struct Flow { +pub struct Flow<'a> { /// Operator on flow/set. pub op: SetOp, /// The [flow table][crate::schema::FlowTable]'s name. - pub flowtable: String, + pub flowtable: Cow<'a, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Forward a packet to a different destination. -pub struct FWD { +pub struct FWD<'a> { #[serde(skip_serializing_if = "Option::is_none")] /// Interface to forward the packet on. - pub dev: Option, + pub dev: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// Family of addr. pub family: Option, #[serde(skip_serializing_if = "Option::is_none")] /// IP(v6) address to forward the packet to. - pub addr: Option, + pub addr: Option>, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] @@ -236,21 +237,21 @@ pub enum FWDFamily { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Duplicate a packet to a different destination. -pub struct Dup { +pub struct Dup<'a> { /// Address to duplicate packet to. - pub addr: Expression, + pub addr: Expression<'a>, #[serde(skip_serializing_if = "Option::is_none")] /// Interface to duplicate packet on. May be omitted to not specify an interface explicitly. - pub dev: Option, + pub dev: Option>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Perform Network Address Translation. /// Referenced by `SNAT` and `DNAT` statements. -pub struct NAT { +pub struct NAT<'a> { #[serde(skip_serializing_if = "Option::is_none")] /// Address to translate to. - pub addr: Option, + pub addr: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// Family of addr, either ip or ip6. Required in inet table family. pub family: Option, @@ -310,13 +311,13 @@ pub enum RejectType { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Dynamically add/update elements to a set. -pub struct Set { +pub struct Set<'a> { /// Operator on set. pub op: SetOp, /// Set element to add or update. - pub elem: Expression, + pub elem: Expression<'a>, /// Set reference. - pub set: String, + pub set: Cow<'a, str>, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] @@ -330,10 +331,10 @@ pub enum SetOp { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Log the packet. /// All properties are optional. -pub struct Log { +pub struct Log<'a> { #[serde(skip_serializing_if = "Option::is_none")] /// Prefix for log entries. - pub prefix: Option, + pub prefix: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// Log group. @@ -360,8 +361,8 @@ pub struct Log { pub flags: Option>, } -impl Log { - pub fn new(group: Option) -> Log { +impl Log<'_> { + pub fn new(group: Option) -> Self { Log { prefix: None, group, @@ -406,22 +407,22 @@ pub enum LogFlag { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Apply a given statement using a meter. -pub struct Meter { +pub struct Meter<'a> { /// Meter name. - pub name: String, + pub name: Cow<'a, str>, /// Meter key. - pub key: Expression, + pub key: Expression<'a>, /// Meter statement. - pub stmt: Box, + pub stmt: Box>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] /// Queue the packet to userspace. -pub struct Queue { +pub struct Queue<'a> { /// Queue number. - pub num: Expression, + pub num: Expression<'a>, #[serde(skip_serializing_if = "Option::is_none")] /// Queue flags. @@ -439,20 +440,20 @@ pub enum QueueFlag { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "vmap")] /// Apply a verdict conditionally. -pub struct VerdictMap { +pub struct VerdictMap<'a> { /// Map key. - pub key: Expression, + pub key: Expression<'a>, /// Mapping expression consisting of value/verdict pairs. - pub data: Expression, + pub data: Expression<'a>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename = "ct count")] /// Limit the number of connections using conntrack. -pub struct CTCount { +pub struct CTCount<'a> { /// Connection count threshold. - pub val: Expression, + pub val: Expression<'a>, #[serde(skip_serializing_if = "Option::is_none")] /// If `true`, match if `val` was exceeded. If omitted, defaults to `false`. @@ -478,12 +479,12 @@ pub struct SynProxy { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] /// Redirects the packet to a local socket without changing the packet header in any way. -pub struct TProxy { +pub struct TProxy<'a> { #[serde(skip_serializing_if = "Option::is_none")] - pub family: Option, + pub family: Option>, pub port: u16, #[serde(skip_serializing_if = "Option::is_none")] - pub addr: Option, + pub addr: Option>, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] diff --git a/src/visitor.rs b/src/visitor.rs index 524302f..9b5ca2d 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -1,14 +1,18 @@ use serde::{de, Deserialize}; -use std::{collections::HashSet, fmt::Formatter, marker::PhantomData, str::FromStr}; +use std::{borrow::Cow, collections::HashSet, fmt::Formatter, marker::PhantomData, str::FromStr}; use crate::stmt::LogFlag; -/// Deserialize null, a string, or string sequence into an `Option>`. -pub fn single_string_to_option_vec<'de, D>(deserializer: D) -> Result>, D::Error> +type CowCowStrs<'a> = Cow<'a, [Cow<'a, str>]>; + +/// Deserialize null, a string, or string sequence into an `Option]>>`. +pub fn single_string_to_option_vec<'a, 'de, D>( + deserializer: D, +) -> Result>, D::Error> where D: de::Deserializer<'de>, { - match single_string_to_vec::<'de, D>(deserializer) { + match single_string_to_vec::<'a, 'de, D>(deserializer) { Ok(value) => match value.len() { 0 => Ok(None), _ => Ok(Some(value)), @@ -17,14 +21,14 @@ where } } -/// Deserialize null, a string or string sequence into a `Vec`. -pub fn single_string_to_vec<'de, D>(deserializer: D) -> Result, D::Error> +/// Deserialize null, a string or string sequence into a `Cow<'a, [Cow<'a, str>]>`. +pub fn single_string_to_vec<'a, 'de, D>(deserializer: D) -> Result, D::Error> where D: de::Deserializer<'de>, { - struct StringOrVec(PhantomData>); - impl<'de> de::Visitor<'de> for StringOrVec { - type Value = Vec; + struct StringOrVec<'a>(PhantomData>); + impl<'a, 'de> de::Visitor<'de> for StringOrVec<'a> { + type Value = CowCowStrs<'a>; fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { formatter.write_str("single string or list of strings") @@ -34,14 +38,14 @@ where where E: de::Error, { - Ok(vec![]) + Ok([][..].into()) } fn visit_str(self, value: &str) -> Result where E: de::Error, { - Ok(vec![value.to_owned()]) + Ok(Cow::Owned(vec![Cow::Owned(value.to_owned())])) } fn visit_seq(self, visitor: S) -> Result diff --git a/tests/helper_tests.rs b/tests/helper_tests.rs index cdc0ef4..43e111e 100644 --- a/tests/helper_tests.rs +++ b/tests/helper_tests.rs @@ -1,4 +1,4 @@ -use std::vec; +use std::{borrow::Cow, vec}; use nftables::{ batch::Batch, @@ -39,13 +39,13 @@ fn test_nft_args_list_map_set() { // nft should return two list object: metainfo and the set/map let applied = helper::get_current_ruleset( None, - Some(vec!["list", "map", "ip", "test-table-01", "test_map"]), + Some(&["list", "map", "ip", "test-table-01", "test_map"]), ) .unwrap(); assert_eq!(2, applied.objects.len()); let applied = helper::get_current_ruleset( None, - Some(vec!["list", "set", "ip", "test-table-01", "test_set"]), + Some(&["list", "set", "ip", "test-table-01", "test_set"]), ) .unwrap(); assert_eq!(2, applied.objects.len()); @@ -70,7 +70,7 @@ fn test_remove_unknown_table() { let mut batch = Batch::new(); batch.delete(schema::NfListObject::Table(schema::Table { family: types::NfFamily::IP6, - name: "i-do-not-exist".to_string(), + name: "i-do-not-exist".into(), ..Table::default() })); let ruleset = batch.to_nftables(); @@ -80,21 +80,21 @@ fn test_remove_unknown_table() { assert!(matches!(err, NftablesError::NftFailed { .. })); } -fn example_ruleset(with_undo: bool) -> schema::Nftables { +fn example_ruleset(with_undo: bool) -> schema::Nftables<'static> { let mut batch = Batch::new(); // create table "test-table-01" - let table_name = "test-table-01".to_string(); + let table_name = "test-table-01"; batch.add(schema::NfListObject::Table(Table { - name: table_name.clone(), + name: table_name.into(), family: types::NfFamily::IP, ..Table::default() })); // create named set "test_set" - let set_name = "test_set".to_string(); - batch.add(schema::NfListObject::Set(schema::Set { + let set_name = "test_set"; + batch.add(schema::NfListObject::Set(Box::new(schema::Set { family: types::NfFamily::IP, - table: table_name.clone(), - name: set_name.clone(), + table: table_name.into(), + name: set_name.into(), handle: None, set_type: schema::SetTypeValue::Single(schema::SetType::Ipv4Addr), policy: None, @@ -104,12 +104,12 @@ fn example_ruleset(with_undo: bool) -> schema::Nftables { gc_interval: None, size: None, comment: None, - })); + }))); // create named map "test_map" - batch.add(schema::NfListObject::Map(schema::Map { + batch.add(schema::NfListObject::Map(Box::new(schema::Map { family: types::NfFamily::IP, - table: table_name.clone(), - name: "test_map".to_string(), + table: table_name.into(), + name: "test_map".into(), handle: None, map: schema::SetTypeValue::Single(schema::SetType::EtherAddr), set_type: schema::SetTypeValue::Single(schema::SetType::Ipv4Addr), @@ -120,28 +120,28 @@ fn example_ruleset(with_undo: bool) -> schema::Nftables { gc_interval: None, size: None, comment: None, - })); + }))); // add element to set batch.add(schema::NfListObject::Element(schema::Element { family: types::NfFamily::IP, - table: table_name, - name: set_name, - elem: vec![ - expr::Expression::String("127.0.0.1".to_string()), - expr::Expression::String("127.0.0.2".to_string()), - ], + table: table_name.into(), + name: set_name.into(), + elem: Cow::Owned(vec![ + expr::Expression::String("127.0.0.1".into()), + expr::Expression::String("127.0.0.2".into()), + ]), })); if with_undo { batch.delete(schema::NfListObject::Table(schema::Table { family: types::NfFamily::IP, - name: "test-table-01".to_string(), + name: "test-table-01".into(), ..Table::default() })); } batch.to_nftables() } -fn get_flush_ruleset() -> schema::Nftables { +fn get_flush_ruleset() -> schema::Nftables<'static> { let mut batch = Batch::new(); batch.add_cmd(schema::NfCmd::Flush(schema::FlushObject::Ruleset(None))); batch.to_nftables() diff --git a/tests/json_tests.rs b/tests/json_tests.rs index 8bbb26c..9d50361 100644 --- a/tests/json_tests.rs +++ b/tests/json_tests.rs @@ -2,6 +2,7 @@ use nftables::expr::{self, Expression, Meta, MetaKey, NamedExpression}; use nftables::stmt::{self, Counter, Match, Operator, Queue, Statement}; use nftables::{schema::*, types::*}; use serde_json::json; +use std::borrow::Cow; #[test] fn test_chain_table_rule_inet() { @@ -12,16 +13,16 @@ fn test_chain_table_rule_inet() { // '{ type filter hook forward priority 0; policy accept; }'" // ``` let expected: Nftables = Nftables { - objects: vec![ + objects: Cow::Borrowed(&[ NfObject::CmdObject(NfCmd::Add(NfListObject::Table(Table { family: NfFamily::INet, - name: "some_inet_table".to_string(), + name: Cow::Borrowed("some_inet_table"), handle: None, }))), NfObject::CmdObject(NfCmd::Add(NfListObject::Chain(Chain { family: NfFamily::INet, - table: "some_inet_table".to_string(), - name: "some_inet_chain".to_string(), + table: Cow::Borrowed("some_inet_table"), + name: Cow::Borrowed("some_inet_chain"), newname: None, handle: None, _type: Some(NfChainType::Filter), @@ -30,7 +31,7 @@ fn test_chain_table_rule_inet() { dev: None, policy: Some(NfChainPolicy::Accept), }))), - ], + ]), }; let json = json!({"nftables":[ {"add":{"table":{"family":"inet","name":"some_inet_table"}}}, @@ -53,25 +54,25 @@ fn test_flowtable() { // add rule inet some_inet_table forward ct state established flow add @flowed' // ``` let expected: Nftables = Nftables { - objects: vec![ - NfObject::ListObject(Box::new(NfListObject::Table(Table { + objects: Cow::Borrowed(&[ + NfObject::ListObject(NfListObject::Table(Table { family: NfFamily::INet, - name: "some_inet_table".to_string(), + name: Cow::Borrowed("some_inet_table"), handle: None, - }))), - NfObject::ListObject(Box::new(NfListObject::FlowTable(FlowTable { + })), + NfObject::ListObject(NfListObject::FlowTable(FlowTable { family: NfFamily::INet, - table: "some_inet_table".to_string(), - name: "flowed".to_string(), + table: Cow::Borrowed("some_inet_table"), + name: Cow::Borrowed("flowed"), handle: None, hook: Some(NfHook::Ingress), prio: Some(0), - dev: Some(vec!["lo".to_string()]), - }))), - NfObject::ListObject(Box::new(NfListObject::Chain(Chain { + dev: Some(Cow::Borrowed(&[Cow::Borrowed("lo")])), + })), + NfObject::ListObject(NfListObject::Chain(Chain { family: NfFamily::INet, - table: "some_inet_table".to_string(), - name: "some_inet_chain".to_string(), + table: Cow::Borrowed("some_inet_table"), + name: Cow::Borrowed("some_inet_chain"), newname: None, handle: None, _type: Some(NfChainType::Filter), @@ -79,31 +80,31 @@ fn test_flowtable() { prio: None, dev: None, policy: Some(NfChainPolicy::Accept), - }))), - NfObject::ListObject(Box::new(NfListObject::Rule(Rule { + })), + NfObject::ListObject(NfListObject::Rule(Rule { family: NfFamily::INet, - table: "some_inet_table".to_string(), - chain: "some_inet_chain".to_string(), - expr: vec![ + table: Cow::Borrowed("some_inet_table"), + chain: Cow::Borrowed("some_inet_chain"), + expr: Cow::Borrowed(&[ Statement::Flow(stmt::Flow { op: stmt::SetOp::Add, - flowtable: "@flowed".to_string(), + flowtable: Cow::Borrowed("@flowed"), }), Statement::Match(Match { left: Expression::Named(NamedExpression::CT(expr::CT { - key: "state".to_string(), + key: Cow::Borrowed("state"), family: None, dir: None, })), op: Operator::IN, - right: Expression::String("established".to_string()), + right: Expression::String(Cow::Borrowed("established")), }), - ], + ]), handle: None, index: None, comment: None, - }))), - ], + })), + ]), }; let json = json!({"nftables":[ {"table":{"family":"inet","name":"some_inet_table"}}, @@ -130,31 +131,33 @@ fn test_insert() { objects: vec![NfObject::CmdObject(NfCmd::Insert(NfListObject::Rule( Rule { family: NfFamily::INet, - table: "some_inet_table".to_string(), - chain: "some_inet_chain".to_string(), + table: "some_inet_table".into(), + chain: "some_inet_chain".into(), expr: vec![ Statement::Match(Match { left: Expression::Named(NamedExpression::Meta(Meta { key: MetaKey::Iifname, })), - right: Expression::String("br-lan".to_string()), + right: Expression::String("br-lan".into()), op: Operator::EQ, }), Statement::Match(Match { left: Expression::Named(NamedExpression::Meta(Meta { key: MetaKey::Oifname, })), - right: Expression::String("wg_exit".to_string()), + right: Expression::String("wg_exit".into()), op: Operator::EQ, }), Statement::Counter(Counter::Anonymous(None)), Statement::Accept(None), - ], + ] + .into(), handle: None, index: Some(0), comment: None, }, - )))], + )))] + .into(), }; let json = json!({"nftables":[{"insert": {"rule":{"family":"inet","table":"some_inet_table","chain":"some_inet_chain","expr":[ @@ -170,16 +173,16 @@ fn test_insert() { #[test] fn test_parsing_of_queue_without_flags() { let expected = Nftables { - objects: vec![NfObject::ListObject(Box::new(NfListObject::Rule(Rule { + objects: Cow::Borrowed(&[NfObject::ListObject(NfListObject::Rule(Rule { family: NfFamily::IP, - table: "test_table".to_string(), - chain: "test_chain".to_string(), - expr: vec![ + table: Cow::Borrowed("test_table"), + chain: Cow::Borrowed("test_chain"), + expr: Cow::Borrowed(&[ Statement::Match(Match { left: Expression::Named(NamedExpression::Payload( nftables::expr::Payload::PayloadField(nftables::expr::PayloadField { - protocol: "udp".to_string(), - field: "dport".to_string(), + protocol: Cow::Borrowed("udp"), + field: Cow::Borrowed("dport"), }), )), right: Expression::Number(20000), @@ -189,11 +192,11 @@ fn test_parsing_of_queue_without_flags() { num: Expression::Number(0), flags: None, }), - ], + ]), handle: Some(2), index: None, comment: None, - })))], + }))]), }; let json = json!({ @@ -246,16 +249,16 @@ fn test_queue_json_serialisation() { #[test] fn test_parse_payload() { let expected = Nftables { - objects: vec![NfObject::ListObject(Box::new(NfListObject::Rule(Rule { + objects: Cow::Borrowed(&[NfObject::ListObject(NfListObject::Rule(Rule { family: NfFamily::IP, - table: "test_table".to_string(), - chain: "test_chain".to_string(), - expr: vec![ + table: Cow::Borrowed("test_table"), + chain: Cow::Borrowed("test_chain"), + expr: Cow::Borrowed(&[ Statement::Match(Match { left: Expression::Named(NamedExpression::Payload( nftables::expr::Payload::PayloadField(nftables::expr::PayloadField { - protocol: "udp".to_string(), - field: "dport".to_string(), + protocol: Cow::Borrowed("udp"), + field: Cow::Borrowed("dport"), }), )), right: Expression::Number(20000), @@ -272,11 +275,11 @@ fn test_parse_payload() { right: Expression::Number(20), op: Operator::EQ, }), - ], + ]), handle: Some(2), index: None, comment: None, - })))], + }))]), }; let json = json!({ diff --git a/tests/serialize.rs b/tests/serialize.rs index 51c9466..97f15eb 100644 --- a/tests/serialize.rs +++ b/tests/serialize.rs @@ -1,18 +1,19 @@ use nftables::{expr::*, schema::*, stmt::*, types::*}; +use std::borrow::Cow; #[test] fn test_serialize() { let _a: Nftables = Nftables { - objects: vec![ + objects: Cow::Owned(vec![ NfObject::CmdObject(NfCmd::Add(NfListObject::Table(Table { family: NfFamily::INet, - name: "namib".to_string(), + name: Cow::Borrowed("namib"), handle: None, }))), NfObject::CmdObject(NfCmd::Add(NfListObject::Chain(Chain { family: NfFamily::INet, - table: "namib".to_string(), - name: "one_chain".to_string(), + table: Cow::Borrowed("namib"), + name: Cow::Borrowed("one_chain"), newname: None, handle: None, _type: Some(NfChainType::Filter), @@ -23,28 +24,28 @@ fn test_serialize() { }))), NfObject::CmdObject(NfCmd::Add(NfListObject::Rule(Rule { family: NfFamily::INet, - table: "namib".to_string(), - chain: "one_chain".to_string(), - expr: vec![ + table: Cow::Borrowed("namib"), + chain: Cow::Borrowed("one_chain"), + expr: Cow::Owned(vec![ Statement::Match(Match { left: Expression::List(vec![ Expression::Number(123), - Expression::String("asd".to_string()), + Expression::String(Cow::Borrowed("asd")), ]), right: Expression::Named(NamedExpression::CT(CT { - key: "state".to_string(), + key: Cow::Borrowed("state"), family: None, dir: None, })), op: Operator::EQ, }), Statement::Drop(Some(Drop {})), - ], + ]), handle: None, index: None, comment: None, }))), - ], + ]), }; let j = serde_json::to_string(&_a).unwrap();