From fb7056b47cc046ccf11be787eb33a31ac7b393ad Mon Sep 17 00:00:00 2001 From: Eric Long Date: Fri, 15 Nov 2024 13:20:08 +0800 Subject: [PATCH 1/7] feat(helper)!: make helper APIs accept borrowed values --- src/helper.rs | 26 ++++++++++---------------- tests/helper_tests.rs | 4 ++-- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/helper.rs b/src/helper.rs index 1f4de22..9f1afd4 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -32,7 +32,7 @@ pub enum NftablesError { pub fn get_current_ruleset( program: Option<&str>, - args: Option>, + args: Option<&[&str]>, ) -> Result { let output = get_current_ruleset_raw(program, args)?; serde_json::from_str(&output).map_err(NftablesError::NftInvalidJson) @@ -40,12 +40,12 @@ pub fn get_current_ruleset( 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/tests/helper_tests.rs b/tests/helper_tests.rs index cdc0ef4..220d0d5 100644 --- a/tests/helper_tests.rs +++ b/tests/helper_tests.rs @@ -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()); From 7922a4a187e1ecce08db4866e30c1a6536761ec8 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Fri, 15 Nov 2024 13:57:17 +0800 Subject: [PATCH 2/7] feat!: use `Cow` whenever possible instead of owned values A lot of strings and arrays used in nftables structures are static, but currently we need to do allocations everywhere using owned String and Vec. Replace them with Cow so we can pass &'static str or &'static [_] whenever possible, while returning owned value when deserializing. --- src/batch.rs | 8 ++- src/expr.rs | 36 +++++----- src/schema.rs | 162 +++++++++++++++++++++--------------------- src/stmt.rs | 31 ++++---- src/visitor.rs | 24 ++++--- tests/helper_tests.rs | 40 +++++------ tests/json_tests.rs | 103 ++++++++++++++------------- tests/serialize.rs | 27 +++---- 8 files changed, 222 insertions(+), 209 deletions(-) diff --git a/src/batch.rs b/src/batch.rs index b7121ea..52b47ff 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -38,16 +38,18 @@ impl Batch { /// 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))) + 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 } + Nftables { + objects: self.data.into(), + } } } diff --git a/src/expr.rs b/src/expr.rs index e3e5a5c..d050761 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}; @@ -9,11 +9,11 @@ use crate::stmt::{Counter, JumpTarget, Statement}; /// In their most basic form, they are just immediate values represented as a JSON string, integer or boolean type. pub enum Expression { // immediates - String(String), + String(Cow<'static, str>), Number(u32), Boolean(bool), /// List expressions are constructed by plain arrays containing of an arbitrary number of expressions. - List(Vec), + List(Cow<'static, [Expression]>), BinaryOperation(BinaryOperation), Range(Range), @@ -26,10 +26,10 @@ pub enum Expression { /// Wrapper for non-immediate `Expression`s. pub enum NamedExpression { /// Concatenate several expressions. - Concat(Vec), + Concat(Cow<'static, [Expression]>), /// This object constructs an anonymous set. /// For mappings, an array of arrays with exactly two elements is expected. - Set(Vec), + Set(Cow<'static, [SetItem]>), Map(Box), Prefix(Prefix), @@ -87,7 +87,7 @@ pub struct Prefix { /// 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 range: Cow<'static, [Expression]>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -111,8 +111,8 @@ pub struct PayloadRaw { /// 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 protocol: Cow<'static, str>, + pub field: Cow<'static, str>, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] @@ -134,8 +134,8 @@ pub enum PayloadBase { /// 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 name: Cow<'static, str>, + pub field: Cow<'static, str>, pub offset: u32, } @@ -143,16 +143,16 @@ pub struct Exthdr { #[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 name: Cow<'static, str>, + pub field: Cow<'static, 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 name: Cow<'static, str>, + pub field: Cow<'static, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -225,7 +225,7 @@ pub enum RTFamily { #[serde(rename = "ct")] /// Create a reference to packet conntrack data. pub struct CT { - pub key: String, + pub key: Cow<'static, str>, #[serde(skip_serializing_if = "Option::is_none")] pub family: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -365,7 +365,7 @@ pub struct Elem { pub val: Box, pub timeout: Option, pub expires: Option, - pub comment: Option, + pub comment: Option>, pub counter: Option, } @@ -373,7 +373,7 @@ pub struct Elem { #[serde(rename = "socket")] /// Construct a reference to packet’s socket. pub struct Socket { - pub key: String, + pub key: Cow<'static, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -384,7 +384,7 @@ pub struct Osf { /// 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<'static, str>, /// Do TTL checks on the packet to determine the operating system. pub ttl: OsfTtl, } diff --git a/src/schema.rs b/src/schema.rs index b389177..0d96d8e 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, @@ -18,7 +18,7 @@ use strum_macros::EnumString; pub struct Nftables { /// An array containing [commands](NfCmd) (for input) or [ruleset elements](NfListObject) (for output). #[serde(rename = "nftables")] - pub objects: Vec, + pub objects: Cow<'static, [NfObject]>, } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -28,7 +28,7 @@ pub enum NfObject { /// A command. CmdObject(NfCmd), /// A ruleset element. - ListObject(Box), + ListObject(NfListObject), } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -42,9 +42,9 @@ pub enum NfListObject { /// A rule element. Rule(Rule), /// A set element. - Set(Set), + Set(Box), /// A map element. - Map(Map), + Map(Box), /// An element manipulation. Element(Element), /// A flow table. @@ -124,11 +124,11 @@ pub enum ResetObject { /// A counter to reset. Counter(Counter), /// A list of counters to reset. - Counters(Vec), + Counters(Cow<'static, [Counter]>), /// A quota to reset. Quota(Quota), /// A list of quotas to reset. - Quotas(Vec), + Quotas(Cow<'static, [Quota]>), } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -157,7 +157,7 @@ pub struct Table { /// The table’s [family](NfFamily), e.g. "ip" or "ip6". pub family: NfFamily, /// The table’s name. - pub name: String, + pub name: Cow<'static, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The table’s handle. /// @@ -171,7 +171,7 @@ impl Default for Table { fn default() -> Self { Table { family: DEFAULT_FAMILY, - name: DEFAULT_TABLE.to_string(), + name: DEFAULT_TABLE.into(), handle: None, } } @@ -183,12 +183,12 @@ pub struct Chain { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'static, str>, /// The chain’s name. - pub name: String, + pub name: Cow<'static, 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). @@ -230,8 +230,8 @@ 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, @@ -252,13 +252,13 @@ pub struct Rule { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'static, str>, /// The chain’s name. - pub chain: String, + pub chain: Cow<'static, 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<'static, [Statement]>, #[serde(skip_serializing_if = "Option::is_none")] /// The rule’s handle. /// @@ -272,7 +272,7 @@ 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. @@ -280,9 +280,9 @@ 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: Cow::Borrowed(&[][..]), handle: None, index: None, comment: None, @@ -296,9 +296,9 @@ pub struct Set { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'static, str>, /// The set’s name. - pub name: String, + pub name: Cow<'static, 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, @@ -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,7 +332,7 @@ 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`. @@ -340,8 +340,8 @@ 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, @@ -362,9 +362,9 @@ pub struct Map { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'static, str>, /// The map’s name. - pub name: String, + pub name: Cow<'static, 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, @@ -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,7 +401,7 @@ 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. @@ -409,8 +409,8 @@ 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), @@ -433,7 +433,7 @@ pub enum SetTypeValue { /// Single set type. Single(SetType), /// Concatenated set types. - Concatenated(Vec), + Concatenated(Cow<'static, [SetType]>), } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, EnumString)] @@ -511,13 +511,13 @@ pub struct Element { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'static, str>, /// The set’s name. - pub name: String, + pub name: Cow<'static, 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<'static, [Expression]>, } /// Default manipulation element for [set](Set) "myset". @@ -525,9 +525,9 @@ 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: Cow::Borrowed(&[][..]), } } } @@ -541,9 +541,9 @@ pub struct FlowTable { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'static, str>, /// The flow table’s name. - pub name: String, + pub name: Cow<'static, 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,8 +560,8 @@ 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". @@ -569,8 +569,8 @@ 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, @@ -590,9 +590,9 @@ pub struct Counter { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'static, str>, /// The counter’s name. - pub name: String, + pub name: Cow<'static, 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, @@ -608,8 +608,8 @@ 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, @@ -633,9 +633,9 @@ pub struct Quota { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'static, str>, /// The quota’s name. - pub name: String, + pub name: Cow<'static, 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, @@ -655,8 +655,8 @@ 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, @@ -674,21 +674,21 @@ pub struct CTHelper { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'static, str>, /// The ct helper’s name. - pub name: String, + pub name: Cow<'static, 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<'static, 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". @@ -696,10 +696,10 @@ 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, } @@ -719,9 +719,9 @@ pub struct Limit { /// The [table](Table)’s family. pub family: NfFamily, /// The [table](Table)’s name. - pub table: String, + pub table: Cow<'static, str>, /// The limit’s name. - pub name: String, + pub name: Cow<'static, 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, @@ -748,8 +748,8 @@ 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, @@ -772,7 +772,7 @@ pub enum LimitUnit { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Meter { - pub name: String, + pub name: Cow<'static, str>, pub key: Expression, pub stmt: Statement, } @@ -795,10 +795,10 @@ impl Default for Ruleset { pub struct MetainfoObject { #[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. /// @@ -831,9 +831,9 @@ pub struct CTTimeout { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'static, str>, /// The ct timeout object’s name. - pub name: String, + pub name: Cow<'static, 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,13 +842,13 @@ 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" @@ -856,8 +856,8 @@ 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, @@ -875,15 +875,15 @@ pub struct CTExpectation { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'static, str>, /// The ct expectation object’s name. - pub name: String, + pub name: Cow<'static, 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, @@ -909,9 +909,9 @@ pub struct SynProxy { /// The table’s family. pub family: NfFamily, /// The table’s name. - pub table: String, + pub table: Cow<'static, str>, /// The synproxy's name. - pub name: String, + pub name: Cow<'static, 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..dbd4124 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")] @@ -56,7 +57,7 @@ pub enum Statement { #[serde(rename = "ct helper")] /// Enable the specified conntrack helper for this packet. - CTHelper(String), // CT helper reference. + CTHelper(Cow<'static, str>), // CT helper reference. Meter(Meter), Queue(Queue), @@ -104,7 +105,7 @@ pub struct Return {} #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct JumpTarget { - pub target: String, + pub target: Cow<'static, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -126,7 +127,7 @@ pub struct Match { /// Anonymous or named Counter. pub enum Counter { /// A counter referenced by name. - Named(String), + Named(Cow<'static, str>), /// An anonymous counter. Anonymous(Option), } @@ -160,7 +161,7 @@ pub enum QuotaOrQuotaRef { /// Anonymous quota object. Quota(Quota), /// Reference to a named quota object. - QuotaRef(String), + QuotaRef(Cow<'static, str>), } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -169,13 +170,13 @@ pub struct Quota { /// 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<'static, 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, @@ -188,16 +189,16 @@ pub struct Limit { 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, @@ -209,7 +210,7 @@ pub struct Flow { /// Operator on flow/set. pub op: SetOp, /// The [flow table][crate::schema::FlowTable]'s name. - pub flowtable: String, + pub flowtable: Cow<'static, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -316,7 +317,7 @@ pub struct Set { /// Set element to add or update. pub elem: Expression, /// Set reference. - pub set: String, + pub set: Cow<'static, str>, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] @@ -333,7 +334,7 @@ pub enum SetOp { pub struct Log { #[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. @@ -408,7 +409,7 @@ pub enum LogFlag { /// Apply a given statement using a meter. pub struct Meter { /// Meter name. - pub name: String, + pub name: Cow<'static, str>, /// Meter key. pub key: Expression, @@ -480,10 +481,10 @@ pub struct SynProxy { /// Redirects the packet to a local socket without changing the packet header in any way. pub struct TProxy { #[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..9288aee 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -1,10 +1,14 @@ 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 = Cow<'static, [Cow<'static, str>]>; + +/// Deserialize null, a string, or string sequence into an `Option]>>`. +pub fn single_string_to_option_vec<'de, D>( + deserializer: D, +) -> Result, D::Error> where D: de::Deserializer<'de>, { @@ -17,14 +21,16 @@ 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<'static, [Cow<'static, str>]>`. +pub fn single_string_to_vec<'de, D>( + deserializer: D, +) -> Result where D: de::Deserializer<'de>, { - struct StringOrVec(PhantomData>); + struct StringOrVec(PhantomData); impl<'de> de::Visitor<'de> for StringOrVec { - type Value = Vec; + type Value = CowCowStrs; fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { formatter.write_str("single string or list of strings") @@ -34,14 +40,14 @@ where where E: de::Error, { - Ok(vec![]) + Ok(Cow::Borrowed(&[][..])) } 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 220d0d5..eeb9db2 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, @@ -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(); @@ -83,18 +83,18 @@ fn test_remove_unknown_table() { fn example_ruleset(with_undo: bool) -> schema::Nftables { 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,21 +120,21 @@ 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::Borrowed(&[ + expr::Expression::String(Cow::Borrowed("127.0.0.1")), + expr::Expression::String(Cow::Borrowed("127.0.0.2")), + ]), })); 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() })); } 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..e068773 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::Borrowed(&[ 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::Borrowed(&[ Statement::Match(Match { - left: Expression::List(vec![ + left: Expression::List(Cow::Borrowed(&[ 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(); From 6f5f339907f28791158fe277f4b673f88ae1c1ec Mon Sep 17 00:00:00 2001 From: Eric Long Date: Fri, 15 Nov 2024 14:52:41 +0800 Subject: [PATCH 3/7] feat!: reduce stack usage by selectively wrapping large values in Box This evens out some of the large enums' sizes (e.g. Expression and Statement's) from over 200 bytes to <=144. Further reduction could be done, as this commit only changes the obvious ones for now. Also moves BinaryOperation's Box up one level so it only uses one Box. --- src/expr.rs | 12 ++++++------ src/schema.rs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index d050761..b39c5e5 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -14,7 +14,7 @@ pub enum Expression { Boolean(bool), /// List expressions are constructed by plain arrays containing of an arbitrary number of expressions. List(Cow<'static, [Expression]>), - BinaryOperation(BinaryOperation), + BinaryOperation(Box), Range(Range), Named(NamedExpression), @@ -327,23 +327,23 @@ pub enum FibFlag { pub enum BinaryOperation { #[serde(rename = "&")] /// Binary AND (`&`) - AND(Box, Box), + AND(Expression, Expression), #[serde(rename = "|")] /// Binary OR (`|`) - OR(Box, Box), + OR(Expression, Expression), #[serde(rename = "^")] /// Binary XOR (`^`) - XOR(Box, Box), + XOR(Expression, Expression), #[serde(rename = "<<")] /// Left shift (`<<`) - LSHIFT(Box, Box), + LSHIFT(Expression, Expression), #[serde(rename = ">>")] /// Right shift (`>>`) - RSHIFT(Box, Box), + RSHIFT(Expression, Expression), } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] diff --git a/src/schema.rs b/src/schema.rs index 0d96d8e..91d4724 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -140,9 +140,9 @@ pub enum FlushObject { /// A chain to flush (i.e., remove all rules from chain). Chain(Chain), /// 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), /// Flush the live ruleset (i.e., remove all elements from live ruleset). @@ -774,7 +774,7 @@ pub enum LimitUnit { pub struct Meter { pub name: Cow<'static, str>, pub key: Expression, - pub stmt: Statement, + pub stmt: Box, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] From ec094c75a9430296ab608be211661d27e44d7be4 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Fri, 15 Nov 2024 15:04:43 +0800 Subject: [PATCH 4/7] feat(expr)!: make range fixed-sized array, not slice Since we only expect two elements from Range, we can just use array instead. --- src/expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index b39c5e5..df90cd8 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -15,7 +15,7 @@ pub enum Expression { /// List expressions are constructed by plain arrays containing of an arbitrary number of expressions. List(Cow<'static, [Expression]>), BinaryOperation(Box), - Range(Range), + Range(Box), Named(NamedExpression), Verdict(Verdict), @@ -87,7 +87,7 @@ pub struct Prefix { /// Construct a range of values. /// The first array item denotes the lower boundary, the second one the upper boundary. pub struct Range { - pub range: Cow<'static, [Expression]>, + pub range: [Expression; 2], } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] From a1c639225734bc05836d89f165c7d0a5bdf7a747 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Sun, 24 Nov 2024 15:43:23 +0800 Subject: [PATCH 5/7] fix(expr)!: revert recursive Cow<[Expression]> back to Vec This fails to build on 1.65: ``` error[E0277]: the trait bound `[SetItem]: ToOwned` is not satisfied in `Expression` --> src/stmt.rs:245:14 | 245 | pub dev: Option, | ^^^^^^^^^^^^^^^^^^ within `Expression`, the trait `ToOwned` is not implemented for `[SetItem]`, which is required by `Expression: Sized` | = help: the trait `ToOwned` is implemented for `[T]` note: required because it appears within the type `Expression` --> src/expr.rs:10:10 | 10 | pub enum Expression { | ^^^^^^^^^^ note: required by an implicit `Sized` bound in `std::option::Option` --> /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/option.rs:572:1 ...and many, many similar errors ``` It compiles successfully on Rust >=1.79, so this commit can be reverted once our MSRV reaches it. --- src/expr.rs | 6 +++--- tests/serialize.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index df90cd8..881408b 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -13,7 +13,7 @@ pub enum Expression { Number(u32), Boolean(bool), /// List expressions are constructed by plain arrays containing of an arbitrary number of expressions. - List(Cow<'static, [Expression]>), + List(Vec), BinaryOperation(Box), Range(Box), @@ -26,10 +26,10 @@ pub enum Expression { /// Wrapper for non-immediate `Expression`s. pub enum NamedExpression { /// Concatenate several expressions. - Concat(Cow<'static, [Expression]>), + Concat(Vec), /// This object constructs an anonymous set. /// For mappings, an array of arrays with exactly two elements is expected. - Set(Cow<'static, [SetItem]>), + Set(Vec), Map(Box), Prefix(Prefix), diff --git a/tests/serialize.rs b/tests/serialize.rs index e068773..97f15eb 100644 --- a/tests/serialize.rs +++ b/tests/serialize.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; #[test] fn test_serialize() { let _a: Nftables = Nftables { - objects: Cow::Borrowed(&[ + objects: Cow::Owned(vec![ NfObject::CmdObject(NfCmd::Add(NfListObject::Table(Table { family: NfFamily::INet, name: Cow::Borrowed("namib"), @@ -26,12 +26,12 @@ fn test_serialize() { family: NfFamily::INet, table: Cow::Borrowed("namib"), chain: Cow::Borrowed("one_chain"), - expr: Cow::Borrowed(&[ + expr: Cow::Owned(vec![ Statement::Match(Match { - left: Expression::List(Cow::Borrowed(&[ + left: Expression::List(vec![ Expression::Number(123), Expression::String(Cow::Borrowed("asd")), - ])), + ]), right: Expression::Named(NamedExpression::CT(CT { key: Cow::Borrowed("state"), family: None, From f037160780b4b6fe0313d452498d07b527b0e9cd Mon Sep 17 00:00:00 2001 From: Eric Long Date: Mon, 25 Nov 2024 11:35:51 +0800 Subject: [PATCH 6/7] feat!: replace Cow<'static, _> with 'a This adds a bunch of lifetimes, but it works with `.into()` when a borrowed value's array container is stored in another place, without the need of `Cow::Borrowed`: ``` let objects = [ NfObject::ListObject(NfListObject::Table(Table { family: NfFamily::INet, name: "some_inet_table".into(), // here handle: None, })), // ... ]; let expected = Nftables { objects: objects.into(); // and here }; ``` However, `Cow::{Borrowed, Owned}` still needs to be used when you want to write the above in one statement. --- src/batch.rs | 22 ++-- src/expr.rs | 134 +++++++++++----------- src/helper.rs | 2 +- src/schema.rs | 252 +++++++++++++++++++++--------------------- src/stmt.rs | 152 ++++++++++++------------- src/visitor.rs | 24 ++-- tests/helper_tests.rs | 10 +- 7 files changed, 297 insertions(+), 299 deletions(-) diff --git a/src/batch.rs b/src/batch.rs index 52b47ff..935bb8c 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -4,50 +4,50 @@ 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) { + 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: I) { + pub fn add_all>>(&mut self, objs: I) { self.data.extend(objs) } /// Wraps Batch in nftables object. - pub fn to_nftables(self) -> Nftables { + pub fn to_nftables(self) -> Nftables<'a> { Nftables { objects: self.data.into(), } diff --git a/src/expr.rs b/src/expr.rs index 881408b..9e343dc 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -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(Cow<'static, str>), + 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(Box), - Range(Box), + 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: [Expression; 2], +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: Cow<'static, str>, - pub field: Cow<'static, str>, +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: Cow<'static, str>, - pub field: Cow<'static, str>, +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: Cow<'static, str>, - pub field: Cow<'static, str>, +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: Cow<'static, str>, - pub field: Cow<'static, str>, +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: Cow<'static, str>, +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(Expression, Expression), + AND(Expression<'a>, Expression<'a>), #[serde(rename = "|")] /// Binary OR (`|`) - OR(Expression, Expression), + OR(Expression<'a>, Expression<'a>), #[serde(rename = "^")] /// Binary XOR (`^`) - XOR(Expression, Expression), + XOR(Expression<'a>, Expression<'a>), #[serde(rename = "<<")] /// Left shift (`<<`) - LSHIFT(Expression, Expression), + LSHIFT(Expression<'a>, Expression<'a>), #[serde(rename = ">>")] /// Right shift (`>>`) - RSHIFT(Expression, Expression), + 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: Cow<'static, str>, +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: Cow<'static, str>, + 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 9f1afd4..c996eb7 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -33,7 +33,7 @@ pub enum NftablesError { pub fn get_current_ruleset( program: Option<&str>, args: Option<&[&str]>, -) -> Result { +) -> Result, NftablesError> { let output = get_current_ruleset_raw(program, args)?; serde_json::from_str(&output).map_err(NftablesError::NftInvalidJson) } diff --git a/src/schema.rs b/src/schema.rs index 91d4724..b1e9cf9 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -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: Cow<'static, [NfObject]>, + 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(NfListObject), + 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(Box), + Set(Box>), /// A map element. - Map(Box), + 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(Cow<'static, [Counter]>), + Counters(Cow<'a, [Counter<'a>]>), /// A quota to reset. - Quota(Quota), + Quota(Quota<'a>), /// A list of quotas to reset. - Quotas(Cow<'static, [Quota]>), + 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(Box), + Set(Box>), /// A map to flush (i.e., remove all elements from map). - Map(Box), + 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: Cow<'static, str>, + pub name: Cow<'a, str>, #[serde(skip_serializing_if = "Option::is_none")] /// The table’s handle. /// @@ -167,7 +167,7 @@ pub struct Table { } /// Default table. -impl Default for Table { +impl Default for Table<'_> { fn default() -> Self { Table { family: DEFAULT_FAMILY, @@ -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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The chain’s name. - pub name: Cow<'static, str>, + 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,7 +226,7 @@ pub struct Chain { } /// Default Chain. -impl Default for Chain { +impl Default for Chain<'_> { fn default() -> Self { Chain { family: DEFAULT_FAMILY, @@ -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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The chain’s name. - pub chain: Cow<'static, str>, + 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: Cow<'static, [Statement]>, + 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.into(), chain: DEFAULT_CHAIN.into(), - expr: Cow::Borrowed(&[][..]), + 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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The set’s name. - pub name: Cow<'static, str>, + 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,11 +332,11 @@ 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, @@ -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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The map’s name. - pub name: Cow<'static, str>, + 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,11 +401,11 @@ 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, @@ -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(Cow<'static, [SetType]>), + 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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The set’s name. - pub name: Cow<'static, str>, + 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: Cow<'static, [Expression]>, + 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.into(), name: "myset".into(), - elem: Cow::Borrowed(&[][..]), + 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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The flow table’s name. - pub name: Cow<'static, str>, + 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, @@ -561,11 +561,11 @@ pub struct FlowTable { /// /// Devices are required for both traffic directions. /// Cow slice of device names, e.g. `vec!["wg0".into(), "wg1".into()].into()`. - pub dev: Option]>>, + pub dev: Option]>>, } /// Default [flowtable](FlowTable) named "myflowtable". -impl Default for FlowTable { +impl Default for FlowTable<'_> { fn default() -> Self { FlowTable { family: DEFAULT_FAMILY, @@ -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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The counter’s name. - pub name: Cow<'static, str>, + 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,7 +604,7 @@ pub struct Counter { } /// Default [counter](Counter) named "mycounter". -impl Default for Counter { +impl Default for Counter<'_> { fn default() -> Self { Counter { family: DEFAULT_FAMILY, @@ -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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The quota’s name. - pub name: Cow<'static, str>, + 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,7 +651,7 @@ pub struct Quota { } /// Default [quota](Quota) named "myquota". -impl Default for Quota { +impl Default for Quota<'_> { fn default() -> Self { Quota { family: DEFAULT_FAMILY, @@ -670,29 +670,29 @@ 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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The ct helper’s name. - pub name: Cow<'static, str>, + 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: Cow<'static, str>, + 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, @@ -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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The limit’s name. - pub name: Cow<'static, str>, + 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,7 +744,7 @@ pub struct Limit { } /// Default [limit](Limit) named "mylimit". -impl Default for Limit { +impl Default for Limit<'_> { fn default() -> Self { Limit { family: DEFAULT_FAMILY, @@ -771,10 +771,10 @@ pub enum LimitUnit { } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct Meter { - pub name: Cow<'static, str>, - pub key: Expression, - pub stmt: Box, +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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The ct timeout object’s name. - pub name: Cow<'static, str>, + 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,17 +842,17 @@ 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, @@ -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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The ct expectation object’s name. - pub name: Cow<'static, str>, + 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: Cow<'static, str>, + pub table: Cow<'a, str>, /// The synproxy's name. - pub name: Cow<'static, str>, + 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 dbd4124..c52dcaf 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -16,7 +16,7 @@ use std::borrow::Cow; /// 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. @@ -26,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(Cow<'static, str>), // 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. @@ -82,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 } @@ -104,8 +104,8 @@ pub struct Continue {} pub struct Return {} #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct JumpTarget { - pub target: Cow<'static, str>, +pub struct JumpTarget<'a> { + pub target: Cow<'a, str>, } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -113,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, } @@ -125,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(Cow<'static, str>), + Named(Cow<'a, str>), /// An anonymous counter. Anonymous(Option), } @@ -147,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(Cow<'static, str>), + 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: Cow<'static, str>, + 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, @@ -184,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, @@ -206,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: Cow<'static, str>, + 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)] @@ -237,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, @@ -311,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: Cow<'static, str>, + pub set: Cow<'a, str>, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] @@ -331,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. @@ -361,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, @@ -407,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: Cow<'static, str>, + 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. @@ -440,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`. @@ -479,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 9288aee..9b5ca2d 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -3,16 +3,16 @@ use std::{borrow::Cow, collections::HashSet, fmt::Formatter, marker::PhantomData use crate::stmt::LogFlag; -type CowCowStrs = Cow<'static, [Cow<'static, str>]>; +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<'de, D>( +/// 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> +) -> 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)), @@ -21,16 +21,14 @@ where } } -/// Deserialize null, a string or string sequence into a `Cow<'static, [Cow<'static, str>]>`. -pub fn single_string_to_vec<'de, D>( - deserializer: D, -) -> Result +/// 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 = CowCowStrs; + 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") @@ -40,7 +38,7 @@ where where E: de::Error, { - Ok(Cow::Borrowed(&[][..])) + Ok([][..].into()) } fn visit_str(self, value: &str) -> Result diff --git a/tests/helper_tests.rs b/tests/helper_tests.rs index eeb9db2..43e111e 100644 --- a/tests/helper_tests.rs +++ b/tests/helper_tests.rs @@ -80,7 +80,7 @@ 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"; @@ -126,9 +126,9 @@ fn example_ruleset(with_undo: bool) -> schema::Nftables { family: types::NfFamily::IP, table: table_name.into(), name: set_name.into(), - elem: Cow::Borrowed(&[ - expr::Expression::String(Cow::Borrowed("127.0.0.1")), - expr::Expression::String(Cow::Borrowed("127.0.0.2")), + elem: Cow::Owned(vec![ + expr::Expression::String("127.0.0.1".into()), + expr::Expression::String("127.0.0.2".into()), ]), })); if with_undo { @@ -141,7 +141,7 @@ fn example_ruleset(with_undo: bool) -> schema::Nftables { 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() From dece88483cbc022a8837c1a9db466d26b9b5193d Mon Sep 17 00:00:00 2001 From: Eric Long Date: Tue, 26 Nov 2024 10:51:36 +0800 Subject: [PATCH 7/7] docs(readme): update examples --- README.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) 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);