diff options
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/batch.rs | 96 | ||||
-rw-r--r-- | src/tests/chain.rs | 120 | ||||
-rw-r--r-- | src/tests/expr.rs | 591 | ||||
-rw-r--r-- | src/tests/mod.rs | 193 | ||||
-rw-r--r-- | src/tests/rule.rs | 132 | ||||
-rw-r--r-- | src/tests/set.rs | 119 | ||||
-rw-r--r-- | src/tests/table.rs | 67 |
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); +} |