aboutsummaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/batch.rs96
-rw-r--r--src/tests/chain.rs120
-rw-r--r--src/tests/expr.rs591
-rw-r--r--src/tests/mod.rs193
-rw-r--r--src/tests/rule.rs132
-rw-r--r--src/tests/set.rs119
-rw-r--r--src/tests/table.rs67
7 files changed, 1318 insertions, 0 deletions
diff --git a/src/tests/batch.rs b/src/tests/batch.rs
new file mode 100644
index 0000000..12f373f
--- /dev/null
+++ b/src/tests/batch.rs
@@ -0,0 +1,96 @@
+use std::mem::size_of;
+
+use libc::{AF_UNSPEC, NFNL_MSG_BATCH_BEGIN, NLM_F_REQUEST};
+use nix::libc::NFNL_MSG_BATCH_END;
+
+use crate::nlmsg::{pad_netlink_object_with_variable_size, NfNetlinkDeserializable};
+use crate::parser::{parse_nlmsg, NlMsg};
+use crate::sys::{nfgenmsg, nlmsghdr, NFNETLINK_V0, NFNL_SUBSYS_NFTABLES};
+use crate::{Batch, MsgType, Table};
+
+use super::get_test_table;
+
+const HEADER_SIZE: u32 =
+ pad_netlink_object_with_variable_size(size_of::<nlmsghdr>() + size_of::<nfgenmsg>()) as u32;
+
+const DEFAULT_BATCH_BEGIN_HDR: nlmsghdr = nlmsghdr {
+ nlmsg_len: HEADER_SIZE,
+ nlmsg_flags: NLM_F_REQUEST as u16,
+ nlmsg_type: NFNL_MSG_BATCH_BEGIN as u16,
+ nlmsg_seq: 0,
+ nlmsg_pid: 0,
+};
+const DEFAULT_BATCH_MSG: NlMsg = NlMsg::NfGenMsg(
+ nfgenmsg {
+ nfgen_family: AF_UNSPEC as u8,
+ version: NFNETLINK_V0 as u8,
+ res_id: NFNL_SUBSYS_NFTABLES as u16,
+ },
+ &[],
+);
+
+const DEFAULT_BATCH_END_HDR: nlmsghdr = nlmsghdr {
+ nlmsg_len: HEADER_SIZE,
+ nlmsg_flags: NLM_F_REQUEST as u16,
+ nlmsg_type: NFNL_MSG_BATCH_END as u16,
+ nlmsg_seq: 1,
+ nlmsg_pid: 0,
+};
+
+#[test]
+fn batch_empty() {
+ let batch = Batch::new();
+ let buf = batch.finalize();
+
+ let (hdr, msg) = parse_nlmsg(&buf).expect("Invalid nlmsg message");
+ assert_eq!(hdr, DEFAULT_BATCH_BEGIN_HDR);
+ assert_eq!(msg, DEFAULT_BATCH_MSG);
+
+ let remaining_data_offset = pad_netlink_object_with_variable_size(hdr.nlmsg_len as usize);
+
+ let (hdr, msg) = parse_nlmsg(&buf[remaining_data_offset..]).expect("Invalid nlmsg message");
+ assert_eq!(hdr, DEFAULT_BATCH_END_HDR);
+ assert_eq!(msg, DEFAULT_BATCH_MSG);
+}
+
+#[test]
+fn batch_with_objects() {
+ let mut original_tables = vec![];
+ for i in 0..10 {
+ let mut table = get_test_table();
+ table.set_userdata(vec![i as u8]);
+ original_tables.push(table);
+ }
+
+ let mut batch = Batch::new();
+ for i in 0..10 {
+ batch.add(
+ &original_tables[i],
+ if i % 2 == 0 {
+ MsgType::Add
+ } else {
+ MsgType::Del
+ },
+ );
+ }
+ let buf = batch.finalize();
+
+ let (hdr, msg) = parse_nlmsg(&buf).expect("Invalid nlmsg message");
+ assert_eq!(hdr, DEFAULT_BATCH_BEGIN_HDR);
+ assert_eq!(msg, DEFAULT_BATCH_MSG);
+ let mut remaining_data = &buf[pad_netlink_object_with_variable_size(hdr.nlmsg_len as usize)..];
+
+ for i in 0..10 {
+ let (deserialized_table, rest) =
+ Table::deserialize(&remaining_data).expect("could not deserialize a table");
+ remaining_data = rest;
+
+ assert_eq!(deserialized_table, original_tables[i]);
+ }
+
+ let (hdr, msg) = parse_nlmsg(&remaining_data).expect("Invalid nlmsg message");
+ let mut end_hdr = DEFAULT_BATCH_END_HDR;
+ end_hdr.nlmsg_seq = 11;
+ assert_eq!(hdr, end_hdr);
+ assert_eq!(msg, DEFAULT_BATCH_MSG);
+}
diff --git a/src/tests/chain.rs b/src/tests/chain.rs
new file mode 100644
index 0000000..7f696e6
--- /dev/null
+++ b/src/tests/chain.rs
@@ -0,0 +1,120 @@
+use crate::{
+ nlmsg::get_operation_from_nlmsghdr_type,
+ sys::{
+ NFTA_CHAIN_HOOK, NFTA_CHAIN_NAME, NFTA_CHAIN_TABLE, NFTA_CHAIN_TYPE, NFTA_CHAIN_USERDATA,
+ NFTA_HOOK_HOOKNUM, NFTA_HOOK_PRIORITY, NFT_MSG_DELCHAIN, NFT_MSG_NEWCHAIN,
+ },
+ ChainType, Hook, HookClass, MsgType,
+};
+
+use super::{
+ get_test_chain, get_test_nlmsg, get_test_nlmsg_with_msg_type, NetlinkExpr, CHAIN_NAME,
+ CHAIN_USERDATA, TABLE_NAME,
+};
+
+#[test]
+fn new_empty_chain() {
+ let mut chain = get_test_chain();
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut chain);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWCHAIN as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 52);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_CHAIN_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_CHAIN_NAME, CHAIN_NAME.as_bytes().to_vec()),
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn new_empty_chain_with_hook_and_type() {
+ let mut chain = get_test_chain()
+ .with_hook(Hook::new(HookClass::In, 0))
+ .with_type(ChainType::Filter);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut chain);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWCHAIN as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 84);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_CHAIN_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_CHAIN_NAME, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_CHAIN_TYPE, "filter".as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_CHAIN_HOOK,
+ vec![
+ NetlinkExpr::List(vec![NetlinkExpr::Final(
+ NFTA_HOOK_HOOKNUM,
+ vec![0, 0, 0, 1]
+ )]),
+ NetlinkExpr::List(vec![NetlinkExpr::Final(
+ NFTA_HOOK_PRIORITY,
+ vec![0, 0, 0, 0]
+ )])
+ ]
+ ),
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn new_empty_chain_with_userdata() {
+ let mut chain = get_test_chain();
+ chain.set_userdata(CHAIN_USERDATA);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut chain);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWCHAIN as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 72);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_CHAIN_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_CHAIN_NAME, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_CHAIN_USERDATA, CHAIN_USERDATA.as_bytes().to_vec())
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn delete_empty_chain() {
+ let mut chain = get_test_chain();
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) =
+ get_test_nlmsg_with_msg_type(&mut buf, &mut chain, MsgType::Del);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_DELCHAIN as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 52);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_CHAIN_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_CHAIN_NAME, CHAIN_NAME.as_bytes().to_vec()),
+ ])
+ .to_raw()
+ );
+}
diff --git a/src/tests/expr.rs b/src/tests/expr.rs
new file mode 100644
index 0000000..35c4fea
--- /dev/null
+++ b/src/tests/expr.rs
@@ -0,0 +1,591 @@
+use std::net::Ipv4Addr;
+
+use libc::NF_DROP;
+
+use crate::{
+ expr::{
+ Bitwise, Cmp, CmpOp, Conntrack, ConntrackKey, Counter, ExpressionList, HeaderField,
+ HighLevelPayload, IcmpCode, Immediate, Log, Lookup, Masquerade, Meta, MetaType, Nat,
+ NatType, Register, Reject, RejectType, TCPHeaderField, TransportHeaderField, VerdictKind,
+ },
+ set::SetBuilder,
+ sys::{
+ NFTA_BITWISE_DREG, NFTA_BITWISE_LEN, NFTA_BITWISE_MASK, NFTA_BITWISE_SREG,
+ NFTA_BITWISE_XOR, NFTA_CMP_DATA, NFTA_CMP_OP, NFTA_CMP_SREG, NFTA_COUNTER_BYTES,
+ NFTA_COUNTER_PACKETS, NFTA_CT_DREG, NFTA_CT_KEY, NFTA_DATA_VALUE, NFTA_DATA_VERDICT,
+ NFTA_EXPR_DATA, NFTA_EXPR_NAME, NFTA_IMMEDIATE_DATA, NFTA_IMMEDIATE_DREG, NFTA_LIST_ELEM,
+ NFTA_LOG_GROUP, NFTA_LOG_PREFIX, NFTA_LOOKUP_SET, NFTA_LOOKUP_SREG, NFTA_META_DREG,
+ NFTA_META_KEY, NFTA_NAT_FAMILY, NFTA_NAT_REG_ADDR_MIN, NFTA_NAT_TYPE, NFTA_PAYLOAD_BASE,
+ NFTA_PAYLOAD_DREG, NFTA_PAYLOAD_LEN, NFTA_PAYLOAD_OFFSET, NFTA_REJECT_ICMP_CODE,
+ NFTA_REJECT_TYPE, NFTA_RULE_CHAIN, NFTA_RULE_EXPRESSIONS, NFTA_RULE_TABLE,
+ NFTA_VERDICT_CODE, NFT_CMP_EQ, NFT_CT_STATE, NFT_META_PROTOCOL, NFT_NAT_SNAT,
+ NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, NFT_REG_VERDICT, NFT_REJECT_ICMPX_UNREACH,
+ },
+ tests::{get_test_table, SET_NAME},
+ ProtocolFamily,
+};
+
+use super::{get_test_nlmsg, get_test_rule, NetlinkExpr, CHAIN_NAME, TABLE_NAME};
+
+#[test]
+fn bitwise_expr_is_valid() {
+ let netmask = Ipv4Addr::new(255, 255, 255, 0);
+ let bitwise = Bitwise::new(netmask.octets(), [0, 0, 0, 0]).unwrap();
+ let mut rule = get_test_rule().with_expressions(ExpressionList::default().with_value(bitwise));
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 124);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"bitwise".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_BITWISE_SREG,
+ NFT_REG_1.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(
+ NFTA_BITWISE_DREG,
+ NFT_REG_1.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(NFTA_BITWISE_LEN, 4u32.to_be_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_BITWISE_MASK,
+ vec![NetlinkExpr::Final(
+ NFTA_DATA_VALUE,
+ vec![255, 255, 255, 0]
+ )]
+ ),
+ NetlinkExpr::Nested(
+ NFTA_BITWISE_XOR,
+ vec![NetlinkExpr::Final(
+ NFTA_DATA_VALUE,
+ 0u32.to_be_bytes().to_vec()
+ )]
+ )
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn cmp_expr_is_valid() {
+ let val = [1u8, 2, 3, 4];
+ let cmp = Cmp::new(CmpOp::Eq, val.clone());
+ let mut rule = get_test_rule().with_expressions(vec![cmp]);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 100);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"cmp".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(NFTA_CMP_SREG, NFT_REG_1.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_CMP_OP, NFT_CMP_EQ.to_be_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_CMP_DATA,
+ vec![NetlinkExpr::Final(NFTA_DATA_VALUE, val.to_vec())]
+ )
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn counter_expr_is_valid() {
+ let nb_bytes = 123456u64;
+ let nb_packets = 987u64;
+ let counter = Counter::default()
+ .with_nb_bytes(nb_bytes)
+ .with_nb_packets(nb_packets);
+
+ let mut rule = get_test_rule().with_expressions(vec![counter]);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 100);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"counter".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_COUNTER_BYTES,
+ nb_bytes.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(
+ NFTA_COUNTER_PACKETS,
+ nb_packets.to_be_bytes().to_vec()
+ )
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn ct_expr_is_valid() {
+ let ct = Conntrack::default().with_retrieve_value(ConntrackKey::State);
+ let mut rule = get_test_rule().with_expressions(vec![ct]);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 88);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"ct".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_CT_KEY,
+ NFT_CT_STATE.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(NFTA_CT_DREG, NFT_REG_1.to_be_bytes().to_vec())
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ )
+}
+
+#[test]
+fn immediate_expr_is_valid() {
+ let immediate = Immediate::new_data(vec![42u8], Register::Reg1);
+ let mut rule =
+ get_test_rule().with_expressions(ExpressionList::default().with_value(immediate));
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 100);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"immediate".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_IMMEDIATE_DREG,
+ NFT_REG_1.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Nested(
+ NFTA_IMMEDIATE_DATA,
+ vec![NetlinkExpr::Final(1u16, 42u8.to_be_bytes().to_vec())]
+ )
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn log_expr_is_valid() {
+ let log = Log::new(Some(1337), Some("mockprefix")).expect("Could not build a log expression");
+ let mut rule = get_test_rule().with_expressions(ExpressionList::default().with_value(log));
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 96);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"log".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(NFTA_LOG_GROUP, 1337u16.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_LOG_PREFIX, b"mockprefix".to_vec()),
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn lookup_expr_is_valid() {
+ let table = get_test_table();
+ let mut set_builder = SetBuilder::new(SET_NAME, &table).unwrap();
+ let address: Ipv4Addr = [8, 8, 8, 8].into();
+ set_builder.add(&address);
+ let (set, _set_elements) = set_builder.finish();
+ let lookup = Lookup::new(&set).unwrap();
+
+ let mut rule = get_test_rule().with_expressions(ExpressionList::default().with_value(lookup));
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 96);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"lookup".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(NFTA_LOOKUP_SET, b"mockset".to_vec()),
+ NetlinkExpr::Final(
+ NFTA_LOOKUP_SREG,
+ NFT_REG_1.to_be_bytes().to_vec()
+ ),
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn masquerade_expr_is_valid() {
+ let masquerade = Masquerade::default();
+ let mut rule = get_test_rule().with_expressions(vec![masquerade]);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 72);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"masq".to_vec()),
+ NetlinkExpr::Nested(NFTA_EXPR_DATA, vec![]),
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn meta_expr_is_valid() {
+ let meta = Meta::default()
+ .with_key(MetaType::Protocol)
+ .with_dreg(Register::Reg1);
+ let mut rule = get_test_rule().with_expressions(vec![meta]);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 88);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"meta".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_META_KEY,
+ NFT_META_PROTOCOL.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(
+ NFTA_META_DREG,
+ NFT_REG_1.to_be_bytes().to_vec()
+ )
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn nat_expr_is_valid() {
+ let nat = Nat::default()
+ .with_nat_type(NatType::SNat)
+ .with_family(ProtocolFamily::Ipv4)
+ .with_ip_register(Register::Reg1);
+ let mut rule = get_test_rule().with_expressions(vec![nat]);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 96);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"nat".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_NAT_TYPE,
+ NFT_NAT_SNAT.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(
+ NFTA_NAT_FAMILY,
+ (ProtocolFamily::Ipv4 as u32).to_be_bytes().to_vec(),
+ ),
+ NetlinkExpr::Final(
+ NFTA_NAT_REG_ADDR_MIN,
+ NFT_REG_1.to_be_bytes().to_vec()
+ )
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn payload_expr_is_valid() {
+ let tcp_header_field = TCPHeaderField::Sport;
+ let transport_header_field = TransportHeaderField::Tcp(tcp_header_field);
+ let payload = HighLevelPayload::Transport(transport_header_field);
+ let mut rule = get_test_rule().with_expressions(vec![payload.build()]);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 108);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"payload".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_PAYLOAD_DREG,
+ NFT_REG_1.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(
+ NFTA_PAYLOAD_BASE,
+ NFT_PAYLOAD_TRANSPORT_HEADER.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(
+ NFTA_PAYLOAD_OFFSET,
+ tcp_header_field.offset().to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(
+ NFTA_PAYLOAD_LEN,
+ tcp_header_field.len().to_be_bytes().to_vec()
+ ),
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn reject_expr_is_valid() {
+ let code = IcmpCode::NoRoute;
+ let reject = Reject::default()
+ .with_type(RejectType::IcmpxUnreach)
+ .with_icmp_code(code);
+ let mut rule = get_test_rule().with_expressions(vec![reject]);
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 92);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"reject".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_REJECT_TYPE,
+ NFT_REJECT_ICMPX_UNREACH.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Final(
+ NFTA_REJECT_ICMP_CODE,
+ (code as u8).to_be_bytes().to_vec()
+ ),
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn verdict_expr_is_valid() {
+ let verdict = Immediate::new_verdict(VerdictKind::Drop);
+ let mut rule = get_test_rule().with_expressions(ExpressionList::default().with_value(verdict));
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(nlmsghdr.nlmsg_len, 104);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_RULE_EXPRESSIONS,
+ vec![NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![
+ NetlinkExpr::Final(NFTA_EXPR_NAME, b"immediate".to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_EXPR_DATA,
+ vec![
+ NetlinkExpr::Final(
+ NFTA_IMMEDIATE_DREG,
+ NFT_REG_VERDICT.to_be_bytes().to_vec()
+ ),
+ NetlinkExpr::Nested(
+ NFTA_IMMEDIATE_DATA,
+ vec![NetlinkExpr::Nested(
+ NFTA_DATA_VERDICT,
+ vec![NetlinkExpr::Final(
+ NFTA_VERDICT_CODE,
+ NF_DROP.to_be_bytes().to_vec()
+ ),]
+ )],
+ ),
+ ]
+ )
+ ]
+ )]
+ )
+ ])
+ .to_raw()
+ );
+}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
new file mode 100644
index 0000000..75fe8b0
--- /dev/null
+++ b/src/tests/mod.rs
@@ -0,0 +1,193 @@
+use crate::data_type::DataType;
+use crate::nlmsg::{NfNetlinkObject, NfNetlinkWriter};
+use crate::parser::{parse_nlmsg, NlMsg};
+use crate::set::{Set, SetBuilder};
+use crate::{sys::*, Chain, MsgType, ProtocolFamily, Rule, Table};
+
+mod batch;
+mod chain;
+mod expr;
+mod rule;
+mod set;
+mod table;
+
+pub const TABLE_NAME: &'static str = "mocktable";
+pub const CHAIN_NAME: &'static str = "mockchain";
+pub const SET_NAME: &'static str = "mockset";
+
+pub const TABLE_USERDATA: &'static str = "mocktabledata";
+pub const CHAIN_USERDATA: &'static str = "mockchaindata";
+pub const RULE_USERDATA: &'static str = "mockruledata";
+pub const SET_USERDATA: &'static str = "mocksetdata";
+
+type NetLinkType = u16;
+
+#[derive(Debug, thiserror::Error)]
+#[error("empty data")]
+pub struct EmptyDataError;
+
+#[derive(Debug, Clone, Eq, Ord)]
+pub enum NetlinkExpr {
+ Nested(NetLinkType, Vec<NetlinkExpr>),
+ Final(NetLinkType, Vec<u8>),
+ List(Vec<NetlinkExpr>),
+}
+
+impl NetlinkExpr {
+ pub fn to_raw(self) -> Vec<u8> {
+ match self.sort() {
+ NetlinkExpr::Final(ty, val) => {
+ let len = val.len() + 4;
+ let mut res = Vec::with_capacity(len);
+
+ res.extend(&(len as u16).to_le_bytes());
+ res.extend(&ty.to_le_bytes());
+ res.extend(val);
+ // alignment
+ while res.len() % 4 != 0 {
+ res.push(0);
+ }
+
+ res
+ }
+ NetlinkExpr::Nested(ty, exprs) => {
+ // some heuristic to decrease allocations (even though this is
+ // only useful for testing so performance is not an objective)
+ let mut sub = Vec::with_capacity(exprs.len() * 50);
+
+ for expr in exprs {
+ sub.append(&mut expr.to_raw());
+ }
+
+ let len = sub.len() + 4;
+ let mut res = Vec::with_capacity(len);
+
+ // set the "NESTED" flag
+ res.extend(&(len as u16).to_le_bytes());
+ res.extend(&(ty | NLA_F_NESTED as u16).to_le_bytes());
+ res.extend(sub);
+
+ res
+ }
+ NetlinkExpr::List(exprs) => {
+ // some heuristic to decrease allocations (even though this is
+ // only useful for testing so performance is not an objective)
+ let mut list = Vec::with_capacity(exprs.len() * 50);
+
+ for expr in exprs {
+ list.append(&mut expr.to_raw());
+ }
+
+ list
+ }
+ }
+ }
+
+ pub fn sort(self) -> Self {
+ match self {
+ NetlinkExpr::Final(_, _) => self,
+ NetlinkExpr::Nested(ty, mut exprs) => {
+ exprs.sort();
+ NetlinkExpr::Nested(ty, exprs)
+ }
+ NetlinkExpr::List(mut exprs) => {
+ exprs.sort();
+ NetlinkExpr::List(exprs)
+ }
+ }
+ }
+}
+
+impl PartialEq for NetlinkExpr {
+ fn eq(&self, other: &Self) -> bool {
+ match (self.clone().sort(), other.clone().sort()) {
+ (NetlinkExpr::Nested(k1, v1), NetlinkExpr::Nested(k2, v2)) => k1 == k2 && v1 == v2,
+ (NetlinkExpr::Final(k1, v1), NetlinkExpr::Final(k2, v2)) => k1 == k2 && v1 == v2,
+ (NetlinkExpr::List(v1), NetlinkExpr::List(v2)) => v1 == v2,
+ _ => false,
+ }
+ }
+}
+
+impl PartialOrd for NetlinkExpr {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ match (self, other) {
+ (
+ NetlinkExpr::Nested(k1, _) | NetlinkExpr::Final(k1, _),
+ NetlinkExpr::Nested(k2, _) | NetlinkExpr::Final(k2, _),
+ ) => k1.partial_cmp(k2),
+ (NetlinkExpr::List(v1), NetlinkExpr::List(v2)) => v1.partial_cmp(v2),
+ (_, NetlinkExpr::List(_)) => Some(std::cmp::Ordering::Less),
+ (NetlinkExpr::List(_), _) => Some(std::cmp::Ordering::Greater),
+ }
+ }
+}
+
+pub fn get_test_table() -> Table {
+ Table::new(ProtocolFamily::Inet)
+ .with_name(TABLE_NAME)
+ .with_flags(0u32)
+}
+
+pub fn get_test_table_raw_expr() -> NetlinkExpr {
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_TABLE_FLAGS, 0u32.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_TABLE_NAME, TABLE_NAME.as_bytes().to_vec()),
+ ])
+ .sort()
+}
+
+pub fn get_test_table_with_userdata_raw_expr() -> NetlinkExpr {
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_TABLE_FLAGS, 0u32.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_TABLE_NAME, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_TABLE_USERDATA, TABLE_USERDATA.as_bytes().to_vec()),
+ ])
+ .sort()
+}
+
+pub fn get_test_chain() -> Chain {
+ Chain::new(&get_test_table()).with_name(CHAIN_NAME)
+}
+
+pub fn get_test_rule() -> Rule {
+ Rule::new(&get_test_chain()).unwrap()
+}
+
+pub fn get_test_set<K: DataType>() -> Set {
+ SetBuilder::<K>::new(SET_NAME, &get_test_table())
+ .expect("Couldn't create a set")
+ .finish()
+ .0
+ .with_userdata(SET_USERDATA)
+}
+
+pub fn get_test_nlmsg_with_msg_type<'a>(
+ buf: &'a mut Vec<u8>,
+ obj: &mut impl NfNetlinkObject,
+ msg_type: MsgType,
+) -> (nlmsghdr, nfgenmsg, &'a [u8]) {
+ let mut writer = NfNetlinkWriter::new(buf);
+ obj.add_or_remove(&mut writer, msg_type, 0);
+
+ let (hdr, msg) = parse_nlmsg(buf.as_slice()).expect("Couldn't parse the message");
+
+ let (nfgenmsg, raw_value) = match msg {
+ NlMsg::NfGenMsg(nfgenmsg, raw_value) => (nfgenmsg, raw_value),
+ _ => panic!("Invalid return value type, expected a valid message"),
+ };
+
+ // sanity checks on the global message (this should be very similar/factorisable for the
+ // most part in other tests)
+ // TODO: check the messages flags
+ assert_eq!(nfgenmsg.res_id.to_be(), 0);
+
+ (hdr, nfgenmsg, raw_value)
+}
+
+pub fn get_test_nlmsg<'a>(
+ buf: &'a mut Vec<u8>,
+ obj: &mut impl NfNetlinkObject,
+) -> (nlmsghdr, nfgenmsg, &'a [u8]) {
+ get_test_nlmsg_with_msg_type(buf, obj, MsgType::Add)
+}
diff --git a/src/tests/rule.rs b/src/tests/rule.rs
new file mode 100644
index 0000000..08b4139
--- /dev/null
+++ b/src/tests/rule.rs
@@ -0,0 +1,132 @@
+use crate::{
+ nlmsg::get_operation_from_nlmsghdr_type,
+ sys::{
+ NFTA_RULE_CHAIN, NFTA_RULE_HANDLE, NFTA_RULE_POSITION, NFTA_RULE_TABLE, NFTA_RULE_USERDATA,
+ NFT_MSG_DELRULE, NFT_MSG_NEWRULE,
+ },
+ MsgType,
+};
+
+use super::{
+ get_test_nlmsg, get_test_nlmsg_with_msg_type, get_test_rule, NetlinkExpr, CHAIN_NAME,
+ RULE_USERDATA, TABLE_NAME,
+};
+
+#[test]
+fn new_empty_rule() {
+ let mut rule = get_test_rule();
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWRULE as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 52);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn new_empty_rule_with_userdata() {
+ let mut rule = get_test_rule().with_userdata(RULE_USERDATA);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWRULE as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 68);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_USERDATA, RULE_USERDATA.as_bytes().to_vec())
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn new_empty_rule_with_position_and_handle() {
+ let handle: u64 = 1337;
+ let position: u64 = 42;
+ let mut rule = get_test_rule().with_handle(handle).with_position(position);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut rule);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWRULE as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 76);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_HANDLE, handle.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_POSITION, position.to_be_bytes().to_vec()),
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn delete_empty_rule() {
+ let mut rule = get_test_rule();
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) =
+ get_test_nlmsg_with_msg_type(&mut buf, &mut rule, MsgType::Del);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_DELRULE as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 52);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn delete_empty_rule_with_handle() {
+ let handle: u64 = 42;
+ let mut rule = get_test_rule().with_handle(handle);
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) =
+ get_test_nlmsg_with_msg_type(&mut buf, &mut rule, MsgType::Del);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_DELRULE as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 64);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_RULE_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_CHAIN, CHAIN_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_RULE_HANDLE, handle.to_be_bytes().to_vec()),
+ ])
+ .to_raw()
+ );
+}
diff --git a/src/tests/set.rs b/src/tests/set.rs
new file mode 100644
index 0000000..6c8247c
--- /dev/null
+++ b/src/tests/set.rs
@@ -0,0 +1,119 @@
+use std::net::{Ipv4Addr, Ipv6Addr};
+
+use crate::{
+ data_type::DataType,
+ nlmsg::get_operation_from_nlmsghdr_type,
+ set::SetBuilder,
+ sys::{
+ NFTA_DATA_VALUE, NFTA_LIST_ELEM, NFTA_SET_ELEM_KEY, NFTA_SET_ELEM_LIST_ELEMENTS,
+ NFTA_SET_ELEM_LIST_SET, NFTA_SET_ELEM_LIST_TABLE, NFTA_SET_KEY_LEN, NFTA_SET_KEY_TYPE,
+ NFTA_SET_NAME, NFTA_SET_TABLE, NFTA_SET_USERDATA, NFT_MSG_DELSET, NFT_MSG_NEWSET,
+ NFT_MSG_NEWSETELEM,
+ },
+ MsgType,
+};
+
+use super::{
+ get_test_nlmsg, get_test_nlmsg_with_msg_type, get_test_set, get_test_table, NetlinkExpr,
+ SET_NAME, SET_USERDATA, TABLE_NAME,
+};
+
+#[test]
+fn new_empty_set() {
+ let mut set = get_test_set::<Ipv4Addr>();
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut set);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWSET as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 80);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_SET_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_NAME, SET_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_KEY_TYPE, Ipv4Addr::TYPE.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_KEY_LEN, Ipv4Addr::LEN.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_USERDATA, SET_USERDATA.as_bytes().to_vec()),
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn delete_empty_set() {
+ let mut set = get_test_set::<Ipv6Addr>();
+
+ let mut buf = Vec::new();
+ let (nlmsghdr, _nfgenmsg, raw_expr) =
+ get_test_nlmsg_with_msg_type(&mut buf, &mut set, MsgType::Del);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_DELSET as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 80);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_SET_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_NAME, SET_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_KEY_TYPE, Ipv6Addr::TYPE.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_KEY_LEN, Ipv6Addr::LEN.to_be_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_USERDATA, SET_USERDATA.as_bytes().to_vec()),
+ ])
+ .to_raw()
+ );
+}
+
+#[test]
+fn new_set_with_data() {
+ let ip1 = Ipv4Addr::new(127, 0, 0, 1);
+ let ip2 = Ipv4Addr::new(1, 1, 1, 1);
+ let mut set_builder = SetBuilder::<Ipv4Addr>::new(SET_NAME.to_string(), &get_test_table())
+ .expect("Couldn't create a set");
+
+ set_builder.add(&ip1);
+ set_builder.add(&ip2);
+ let (_set, mut elem_list) = set_builder.finish();
+
+ let mut buf = Vec::new();
+
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut elem_list);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWSETELEM as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 84);
+
+ assert_eq!(
+ raw_expr,
+ NetlinkExpr::List(vec![
+ NetlinkExpr::Final(NFTA_SET_ELEM_LIST_TABLE, TABLE_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Final(NFTA_SET_ELEM_LIST_SET, SET_NAME.as_bytes().to_vec()),
+ NetlinkExpr::Nested(
+ NFTA_SET_ELEM_LIST_ELEMENTS,
+ vec![
+ NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![NetlinkExpr::Nested(
+ NFTA_DATA_VALUE,
+ vec![NetlinkExpr::Final(NFTA_SET_ELEM_KEY, ip1.data().to_vec())]
+ )]
+ ),
+ NetlinkExpr::Nested(
+ NFTA_LIST_ELEM,
+ vec![NetlinkExpr::Nested(
+ NFTA_DATA_VALUE,
+ vec![NetlinkExpr::Final(NFTA_SET_ELEM_KEY, ip2.data().to_vec())]
+ )]
+ ),
+ ]
+ ),
+ ])
+ .to_raw()
+ );
+}
diff --git a/src/tests/table.rs b/src/tests/table.rs
new file mode 100644
index 0000000..39bf399
--- /dev/null
+++ b/src/tests/table.rs
@@ -0,0 +1,67 @@
+use crate::{
+ nlmsg::{get_operation_from_nlmsghdr_type, nft_nlmsg_maxsize, NfNetlinkDeserializable},
+ sys::{NFT_MSG_DELTABLE, NFT_MSG_NEWTABLE},
+ MsgType, Table,
+};
+
+use super::{
+ get_test_nlmsg, get_test_nlmsg_with_msg_type, get_test_table, get_test_table_raw_expr,
+ get_test_table_with_userdata_raw_expr, TABLE_USERDATA,
+};
+
+#[test]
+fn new_empty_table() {
+ let mut table = get_test_table();
+ let mut buf = Vec::with_capacity(nft_nlmsg_maxsize() as usize);
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut table);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWTABLE as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 44);
+
+ assert_eq!(raw_expr, get_test_table_raw_expr().to_raw());
+}
+
+#[test]
+fn new_empty_table_with_userdata() {
+ let mut table = get_test_table();
+ table.set_userdata(TABLE_USERDATA.as_bytes().to_vec());
+ let mut buf = Vec::with_capacity(nft_nlmsg_maxsize() as usize);
+ let (nlmsghdr, _nfgenmsg, raw_expr) = get_test_nlmsg(&mut buf, &mut table);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_NEWTABLE as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 64);
+
+ assert_eq!(raw_expr, get_test_table_with_userdata_raw_expr().to_raw());
+}
+
+#[test]
+fn delete_empty_table() {
+ let mut table = get_test_table();
+ let mut buf = Vec::with_capacity(nft_nlmsg_maxsize() as usize);
+ let (nlmsghdr, _nfgenmsg, raw_expr) =
+ get_test_nlmsg_with_msg_type(&mut buf, &mut table, MsgType::Del);
+ assert_eq!(
+ get_operation_from_nlmsghdr_type(nlmsghdr.nlmsg_type),
+ NFT_MSG_DELTABLE as u8
+ );
+ assert_eq!(nlmsghdr.nlmsg_len, 44);
+
+ assert_eq!(raw_expr, get_test_table_raw_expr().to_raw());
+}
+
+#[test]
+fn parse_table() {
+ let mut table = get_test_table();
+ table.set_userdata(TABLE_USERDATA.as_bytes().to_vec());
+ let mut buf = Vec::with_capacity(nft_nlmsg_maxsize() as usize);
+ let (_nlmsghdr, _nfgenmsg, _raw_expr) = get_test_nlmsg(&mut buf, &mut table);
+
+ let (deserialized_table, remaining) =
+ Table::deserialize(&buf).expect("Couldn't deserialize the object");
+ assert_eq!(table, deserialized_table);
+ assert_eq!(remaining.len(), 0);
+}