From 384958620abab397062b67fb2763e813b63f74f0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 27 Sep 2012 19:12:53 +0200 Subject: use nf_tables and nf_tables compatibility interface This patch adds the following utilities: * xtables * xtables-restore * xtables-save * xtables-config They all use Patrick's nf_tables infrastructure plus my compatibility layer. xtables, xtables-restore and xtables-save are syntax compatible with ip[6]tables, ip[6]tables-restore and ip[6]tables-save. Semantics aims to be similar, still the main exception is that there is no commit operation. Thus, we incrementally add/delete rules without entire table locking. The following options are also not yet implemented: -Z (this requires adding expr->ops->reset(...) so nft_counters can reset internal state of expressions while dumping it) -R and -E (this requires adding this feature to nf_tables) -f (can be implemented with expressions: payload 6 (2-bytes) + bitwise a&b^!b + cmp neq 0) -IPv6 support. But those are a matter of time to get them done. A new utility, xtables-config, is available to register tables and chains. By default there is a configuration file that adds backward compatible tables and chains under iptables/etc/xtables.conf. You have to call this utility first to register tables and chains. However, it would be possible to automagically register tables and chains while using xtables and xtables-restore to get similar operation than with iptables. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 44 + etc/xtables.conf | 24 + include/iptables.h | 2 + include/linux/netfilter/nf_tables.h | 418 +++++ include/linux/netfilter/nf_tables_compat.h | 20 + include/linux/netfilter/nfnetlink.h | 100 + include/xtables.h | 3 + iptables/Makefile.am | 25 +- iptables/ip6tables.c | 1 + iptables/iptables.c | 1 + iptables/nft.c | 2764 ++++++++++++++++++++++++++++ iptables/nft.h | 62 + iptables/xshared.h | 1 + iptables/xtables-config-parser.y | 213 +++ iptables/xtables-config-syntax.l | 53 + iptables/xtables-config.c | 107 ++ iptables/xtables-multi.c | 10 + iptables/xtables-multi.h | 4 + iptables/xtables-restore.c | 417 +++++ iptables/xtables-save.c | 122 ++ iptables/xtables-standalone.c | 80 + iptables/xtables.c | 1251 +++++++++++++ libxtables/xtables.c | 6 +- 23 files changed, 5723 insertions(+), 5 deletions(-) create mode 100644 etc/xtables.conf create mode 100644 include/linux/netfilter/nf_tables.h create mode 100644 include/linux/netfilter/nf_tables_compat.h create mode 100644 include/linux/netfilter/nfnetlink.h create mode 100644 iptables/nft.c create mode 100644 iptables/nft.h create mode 100644 iptables/xtables-config-parser.y create mode 100644 iptables/xtables-config-syntax.l create mode 100644 iptables/xtables-config.c create mode 100644 iptables/xtables-restore.c create mode 100644 iptables/xtables-save.c create mode 100644 iptables/xtables-standalone.c create mode 100644 iptables/xtables.c diff --git a/configure.ac b/configure.ac index e83304c5..eb2c367b 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,9 @@ AC_ARG_ENABLE([nfsynproxy], AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) +AC_ARG_ENABLE([nftables], + AS_HELP_STRING([--disable-xtables], [Do not build xtables]), + [enable_nftables="$enableval"], [enable_nftables="yes"]) libiptc_LDFLAGS2=""; AX_CHECK_LINKER_FLAGS([-Wl,--no-as-needed], @@ -106,6 +109,7 @@ AM_CONDITIONAL([ENABLE_DEVEL], [test "$enable_devel" = "yes"]) AM_CONDITIONAL([ENABLE_LIBIPQ], [test "$enable_libipq" = "yes"]) AM_CONDITIONAL([ENABLE_BPFC], [test "$enable_bpfc" = "yes"]) AM_CONDITIONAL([ENABLE_SYNCONF], [test "$enable_nfsynproxy" = "yes"]) +AM_CONDITIONAL([ENABLE_NFTABLES], [test "$enable_nftables" = "yes"]) if test "x$enable_bpfc" = "xyes" || test "x$enable_nfsynproxy" = "xyes"; then AC_CHECK_LIB(pcap, pcap_compile,, AC_MSG_ERROR(missing libpcap library required by bpf compiler or nfsynproxy tool)) @@ -115,6 +119,45 @@ PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0], [nfnetlink=1], [nfnetlink=0]) AM_CONDITIONAL([HAVE_LIBNFNETLINK], [test "$nfnetlink" = 1]) +PKG_CHECK_MODULES([libmnl], [libmnl >= 1.0], + [mnl=1], [mnl=0]) +AM_CONDITIONAL([HAVE_LIBMNL], [test "$mnl" = 1]) + +PKG_CHECK_MODULES([libnfables], [libnftables >= 1.0], + [nftables=1], [nftables=0]) +AM_CONDITIONAL([HAVE_LIBNFTABLES], [test "$nftables" = 1]) + +AM_PROG_LEX +AC_PROG_YACC + +if test -z "$ac_cv_prog_YACC" +then + echo "*** Error: No suitable bison/yacc found. ***" + echo " Please install the 'bison' package." + exit 1 +fi +if test -z "$ac_cv_prog_LEX" +then + echo "*** Error: No suitable flex/lex found. ***" + echo " Please install the 'flex' package." + exit 1 +fi + +AC_MSG_CHECKING(flex version) +flex_version=`$ac_cv_prog_LEX --version | sed 's/version//g' | awk '/flex/ {print $2}'` +flex_major=`echo $flex_version| cut -d . -f 1` +flex_minor=`echo $flex_version| cut -d . -f 2` +flex_rev=`echo $flex_version| cut -d . -f 3` + +if test "$flex_major" -eq "2" && test "$flex_minor" -eq "5" && test "$flex_rev" -ge "33"; then + AC_MSG_RESULT([$flex_version. OK]) +else + AC_MSG_WARN([flex version $flex_version found. + Version 2.5.33 or greater is required. You may experience problems + while compilating the conntrack-tools. Please, consider to upgrade + flex.]) +fi + regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \ -Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \ -Winline -pipe"; @@ -182,6 +225,7 @@ Iptables Configuration: Large file support: ${enable_largefile} BPF utils support: ${enable_bpfc} nfsynproxy util support: ${enable_nfsynproxy} + nftables support: ${enable_nftables} Build parameters: Put plugins into executable (static): ${enable_static} diff --git a/etc/xtables.conf b/etc/xtables.conf new file mode 100644 index 00000000..00b5df4f --- /dev/null +++ b/etc/xtables.conf @@ -0,0 +1,24 @@ +table raw prio -300 { + chain PREROUTING hook NF_INET_PRE_ROUTING + chain OUTPUT hook NF_INET_LOCAL_OUT +} + +table mangle prio -150 { + chain PREROUTING hook NF_INET_PRE_ROUTING + chain INPUT hook NF_INET_LOCAL_IN + chain FORWARD hook NF_INET_FORWARD + chain OUTPUT hook NF_INET_LOCAL_OUT + chain POSTROUTING hook NF_INET_POST_ROUTING +} + +table filter prio 0 { + chain INPUT hook NF_INET_LOCAL_IN + chain FORWARD hook NF_INET_FORWARD + chain OUTPUT hook NF_INET_LOCAL_OUT +} + +table security prio 150 { + chain INPUT hook NF_INET_LOCAL_IN + chain FORWARD hook NF_INET_FORWARD + chain OUTPUT hook NF_INET_LOCAL_OUT +} diff --git a/include/iptables.h b/include/iptables.h index ac9dc0e5..78c10abd 100644 --- a/include/iptables.h +++ b/include/iptables.h @@ -20,4 +20,6 @@ extern void print_rule4(const struct ipt_entry *e, extern struct xtables_globals iptables_globals; +extern struct xtables_globals xtables_globals; + #endif /*_IPTABLES_USER_H*/ diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h new file mode 100644 index 00000000..a6d51374 --- /dev/null +++ b/include/linux/netfilter/nf_tables.h @@ -0,0 +1,418 @@ +#ifndef _LINUX_NF_TABLES_H +#define _LINUX_NF_TABLES_H + +enum nft_registers { + NFT_REG_VERDICT, + NFT_REG_1, + NFT_REG_2, + NFT_REG_3, + NFT_REG_4, + __NFT_REG_MAX +}; +#define NFT_REG_MAX (__NFT_REG_MAX - 1) + +enum nft_verdicts { + NFT_CONTINUE = -1, + NFT_BREAK = -2, + NFT_JUMP = -3, + NFT_GOTO = -4, + NFT_RETURN = -5, +}; + +enum nf_tables_msg_types { + NFT_MSG_NEWTABLE, + NFT_MSG_GETTABLE, + NFT_MSG_DELTABLE, + NFT_MSG_NEWCHAIN, + NFT_MSG_GETCHAIN, + NFT_MSG_DELCHAIN, + NFT_MSG_NEWRULE, + NFT_MSG_GETRULE, + NFT_MSG_DELRULE, + NFT_MSG_NEWSET, + NFT_MSG_GETSET, + NFT_MSG_DELSET, + NFT_MSG_NEWSETELEM, + NFT_MSG_GETSETELEM, + NFT_MSG_DELSETELEM, + NFT_MSG_MAX, +}; + +enum nft_list_attributes { + NFTA_LIST_UNPEC, + NFTA_LIST_ELEM, + __NFTA_LIST_MAX +}; +#define NFTA_LIST_MAX (__NFTA_LIST_MAX - 1) + +enum nft_hook_attributes { + NFTA_HOOK_UNSPEC, + NFTA_HOOK_HOOKNUM, + NFTA_HOOK_PRIORITY, + __NFTA_HOOK_MAX +}; +#define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1) + +enum nft_table_attributes { + NFTA_TABLE_UNSPEC, + NFTA_TABLE_NAME, + __NFTA_TABLE_MAX +}; +#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1) + +enum nft_chain_attributes { + NFTA_CHAIN_UNSPEC, + NFTA_CHAIN_TABLE, + NFTA_CHAIN_NAME, + NFTA_CHAIN_HOOK, + __NFTA_CHAIN_MAX +}; +#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) + +enum nft_rule_attributes { + NFTA_RULE_UNSPEC, + NFTA_RULE_TABLE, + NFTA_RULE_CHAIN, + NFTA_RULE_HANDLE, + NFTA_RULE_EXPRESSIONS, + __NFTA_RULE_MAX +}; +#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) + +/** + * enum nft_set_flags - nf_tables set flags + * + * @NFT_SET_ANONYMOUS: name allocation, automatic cleanup on unlink + * @NFT_SET_CONSTANT: set contents may not change while bound + * @NFT_SET_INTERVAL: set contains intervals + * @NFT_SET_MAP: set is used as a dictionary + */ +enum nft_set_flags { + NFT_SET_ANONYMOUS = 0x1, + NFT_SET_CONSTANT = 0x2, + NFT_SET_INTERVAL = 0x4, + NFT_SET_MAP = 0x8, +}; + +/** + * enum nft_set_attributes - nf_tables set netlink attributes + * + * @NFTA_SET_TABLE: table name (NLA_STRING) + * @NFTA_SET_NAME: set name (NLA_STRING) + * @NFTA_SET_FLAGS: bitmask of enum nft_set_flags (NLA_U32) + * @NFTA_SET_KEY_TYPE: key data type, informational purpose only (NLA_U32) + * @NFTA_SET_KEY_LEN: key data length (NLA_U32) + * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32) + * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32) + */ +enum nft_set_attributes { + NFTA_SET_UNSPEC, + NFTA_SET_TABLE, + NFTA_SET_NAME, + NFTA_SET_FLAGS, + NFTA_SET_KEY_TYPE, + NFTA_SET_KEY_LEN, + NFTA_SET_DATA_TYPE, + NFTA_SET_DATA_LEN, + __NFTA_SET_MAX +}; +#define NFTA_SET_MAX (__NFTA_SET_MAX - 1) + +/** + * enum nft_set_elem_flags - nf_tables set element flags + * + * @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval + */ +enum nft_set_elem_flags { + NFT_SET_ELEM_INTERVAL_END = 0x1, +}; + +/** + * enum nft_set_elem_attributes - nf_tables set element netlink attributes + * + * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data) + * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes) + * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32) + */ +enum nft_set_elem_attributes { + NFTA_SET_ELEM_UNSPEC, + NFTA_SET_ELEM_KEY, + NFTA_SET_ELEM_DATA, + NFTA_SET_ELEM_FLAGS, + __NFTA_SET_ELEM_MAX +}; +#define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1) + +/** + * enum nft_set_elem_list_attributes - nf_tables set element list netlink attributes + * + * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING) + * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING) + * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes) + */ +enum nft_set_elem_list_attributes { + NFTA_SET_ELEM_LIST_UNSPEC, + NFTA_SET_ELEM_LIST_TABLE, + NFTA_SET_ELEM_LIST_SET, + NFTA_SET_ELEM_LIST_ELEMENTS, + __NFTA_SET_ELEM_LIST_MAX +}; +#define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1) + +/** + * enum nft_data_types - nf_tables data types + * + * @NFT_DATA_VALUE: generic data + * @NFT_DATA_VERDICT: netfilter verdict + * + * The type of data is usually determined by the kernel directly and is not + * explicitly specified by userspace. The only difference are sets, where + * userspace specifies the key and mapping data types. + * + * The values 0xffffff00-0xffffffff are reserved for internally used types. + * The remaining range can be freely used by userspace to encode types, all + * values are equivalent to NFT_DATA_VALUE. + */ +enum nft_data_types { + NFT_DATA_VALUE, + NFT_DATA_VERDICT = 0xffffff00U, +}; + +#define NFT_DATA_RESERVED_MASK 0xffffff00U + +enum nft_data_attributes { + NFTA_DATA_UNSPEC, + NFTA_DATA_VALUE, + NFTA_DATA_VERDICT, + __NFTA_DATA_MAX +}; +#define NFTA_DATA_MAX (__NFTA_DATA_MAX - 1) + +enum nft_verdict_attributes { + NFTA_VERDICT_UNSPEC, + NFTA_VERDICT_CODE, + NFTA_VERDICT_CHAIN, + __NFTA_VERDICT_MAX +}; +#define NFTA_VERDICT_MAX (__NFTA_VERDICT_MAX - 1) + +enum nft_expr_attributes { + NFTA_EXPR_UNSPEC, + NFTA_EXPR_NAME, + NFTA_EXPR_DATA, + __NFTA_EXPR_MAX +}; +#define NFTA_EXPR_MAX (__NFTA_EXPR_MAX - 1) + +enum nft_immediate_attributes { + NFTA_IMMEDIATE_UNSPEC, + NFTA_IMMEDIATE_DREG, + NFTA_IMMEDIATE_DATA, + __NFTA_IMMEDIATE_MAX +}; +#define NFTA_IMMEDIATE_MAX (__NFTA_IMMEDIATE_MAX - 1) + +enum nft_bitwise_attributes { + NFTA_BITWISE_UNSPEC, + NFTA_BITWISE_SREG, + NFTA_BITWISE_DREG, + NFTA_BITWISE_LEN, + NFTA_BITWISE_MASK, + NFTA_BITWISE_XOR, + __NFTA_BITWISE_MAX +}; +#define NFTA_BITWISE_MAX (__NFTA_BITWISE_MAX - 1) + +enum nft_byteorder_ops { + NFT_BYTEORDER_NTOH, + NFT_BYTEORDER_HTON, +}; + +enum nft_byteorder_attributes { + NFTA_BYTEORDER_UNSPEC, + NFTA_BYTEORDER_SREG, + NFTA_BYTEORDER_DREG, + NFTA_BYTEORDER_OP, + NFTA_BYTEORDER_LEN, + NFTA_BYTEORDER_SIZE, + __NFTA_BYTEORDER_MAX +}; +#define NFTA_BYTEORDER_MAX (__NFTA_BYTEORDER_MAX - 1) + +enum nft_cmp_ops { + NFT_CMP_EQ, + NFT_CMP_NEQ, + NFT_CMP_LT, + NFT_CMP_LTE, + NFT_CMP_GT, + NFT_CMP_GTE, +}; + +enum nft_cmp_attributes { + NFTA_CMP_UNSPEC, + NFTA_CMP_SREG, + NFTA_CMP_OP, + NFTA_CMP_DATA, + __NFTA_CMP_MAX +}; +#define NFTA_CMP_MAX (__NFTA_CMP_MAX - 1) + +enum nft_lookup_attributes { + NFTA_LOOKUP_UNSPEC, + NFTA_LOOKUP_SET, + NFTA_LOOKUP_SREG, + NFTA_LOOKUP_DREG, + __NFTA_LOOKUP_MAX +}; +#define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1) + +enum nft_payload_bases { + NFT_PAYLOAD_LL_HEADER, + NFT_PAYLOAD_NETWORK_HEADER, + NFT_PAYLOAD_TRANSPORT_HEADER, +}; + +enum nft_payload_attributes { + NFTA_PAYLOAD_UNSPEC, + NFTA_PAYLOAD_DREG, + NFTA_PAYLOAD_BASE, + NFTA_PAYLOAD_OFFSET, + NFTA_PAYLOAD_LEN, + __NFTA_PAYLOAD_MAX +}; +#define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1) + +enum nft_exthdr_attributes { + NFTA_EXTHDR_UNSPEC, + NFTA_EXTHDR_DREG, + NFTA_EXTHDR_TYPE, + NFTA_EXTHDR_OFFSET, + NFTA_EXTHDR_LEN, + __NFTA_EXTHDR_MAX +}; +#define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1) + +enum nft_meta_keys { + NFT_META_LEN, + NFT_META_PROTOCOL, + NFT_META_PRIORITY, + NFT_META_MARK, + NFT_META_IIF, + NFT_META_OIF, + NFT_META_IIFNAME, + NFT_META_OIFNAME, + NFT_META_IIFTYPE, + NFT_META_OIFTYPE, + NFT_META_SKUID, + NFT_META_SKGID, + NFT_META_NFTRACE, + NFT_META_RTCLASSID, + NFT_META_SECMARK, +}; + +enum nft_meta_attributes { + NFTA_META_UNSPEC, + NFTA_META_DREG, + NFTA_META_KEY, + __NFTA_META_MAX +}; +#define NFTA_META_MAX (__NFTA_META_MAX - 1) + +enum nft_ct_keys { + NFT_CT_STATE, + NFT_CT_DIRECTION, + NFT_CT_STATUS, + NFT_CT_MARK, + NFT_CT_SECMARK, + NFT_CT_EXPIRATION, + NFT_CT_HELPER, + NFT_CT_PROTOCOL, + NFT_CT_SRC, + NFT_CT_DST, + NFT_CT_PROTO_SRC, + NFT_CT_PROTO_DST, +}; + +enum nft_ct_attributes { + NFTA_CT_UNSPEC, + NFTA_CT_DREG, + NFTA_CT_KEY, + NFTA_CT_DIRECTION, + __NFTA_CT_MAX +}; +#define NFTA_CT_MAX (__NFTA_CT_MAX - 1) + +enum nft_limit_attributes { + NFTA_LIMIT_UNSPEC, + NFTA_LIMIT_RATE, + NFTA_LIMIT_DEPTH, + __NFTA_LIMIT_MAX +}; +#define NFTA_LIMIT_MAX (__NFTA_LIMIT_MAX - 1) + +enum nft_counter_attributes { + NFTA_COUNTER_UNSPEC, + NFTA_COUNTER_BYTES, + NFTA_COUNTER_PACKETS, + __NFTA_COUNTER_MAX +}; +#define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1) + +enum nft_log_attributes { + NFTA_LOG_UNSPEC, + NFTA_LOG_GROUP, + NFTA_LOG_PREFIX, + NFTA_LOG_SNAPLEN, + NFTA_LOG_QTHRESHOLD, + __NFTA_LOG_MAX +}; +#define NFTA_LOG_MAX (__NFTA_LOG_MAX - 1) + +enum nft_reject_types { + NFT_REJECT_ICMP_UNREACH, + NFT_REJECT_TCP_RST, +}; + +enum nft_reject_attributes { + NFTA_REJECT_UNSPEC, + NFTA_REJECT_TYPE, + NFTA_REJECT_ICMP_CODE, + __NFTA_REJECT_MAX +}; +#define NFTA_REJECT_MAX (__NFTA_REJECT_MAX - 1) + +enum nft_nat_types { + NFT_NAT_SNAT, + NFT_NAT_DNAT, +}; + +enum nft_nat_attributes { + NFTA_NAT_UNSPEC, + NFTA_NAT_TYPE, + NFTA_NAT_ADDR_MIN, + NFTA_NAT_ADDR_MAX, + NFTA_NAT_PROTO_MIN, + NFTA_NAT_PROTO_MAX, + __NFTA_NAT_MAX +}; +#define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) + +enum nft_target_attributes { + NFTA_TARGET_UNSPEC, + NFTA_TARGET_NAME, + NFTA_TARGET_REV, + NFTA_TARGET_INFO, + __NFTA_TARGET_MAX +}; +#define NFTA_TARGET_MAX (__NFTA_TARGET_MAX - 1) + +enum nft_match_attributes { + NFTA_MATCH_UNSPEC, + NFTA_MATCH_NAME, + NFTA_MATCH_REV, + NFTA_MATCH_INFO, + __NFTA_MATCH_MAX +}; +#define NFTA_MATCH_MAX (__NFTA_MATCH_MAX - 1) + +#endif /* _LINUX_NF_TABLES_H */ diff --git a/include/linux/netfilter/nf_tables_compat.h b/include/linux/netfilter/nf_tables_compat.h new file mode 100644 index 00000000..36fb81d8 --- /dev/null +++ b/include/linux/netfilter/nf_tables_compat.h @@ -0,0 +1,20 @@ +#ifndef _NFT_COMPAT_NFNETLINK_H_ +#define _NFT_COMPAT_NFNETLINK_H_ + +#define NFT_COMPAT_NAME_MAX 32 + +enum { + NFNL_MSG_COMPAT_GET, + NFNL_MSG_COMPAT_MAX +}; + +enum { + NFTA_COMPAT_UNSPEC = 0, + NFTA_COMPAT_NAME, + NFTA_COMPAT_REV, + NFTA_COMPAT_TYPE, + __NFTA_COMPAT_MAX, +}; +#define NFTA_COMPAT_MAX (__NFTA_COMPAT_MAX - 1) + +#endif diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h new file mode 100644 index 00000000..91eebab4 --- /dev/null +++ b/include/linux/netfilter/nfnetlink.h @@ -0,0 +1,100 @@ +#ifndef _NFNETLINK_H +#define _NFNETLINK_H +#include +#include + +enum nfnetlink_groups { + NFNLGRP_NONE, +#define NFNLGRP_NONE NFNLGRP_NONE + NFNLGRP_CONNTRACK_NEW, +#define NFNLGRP_CONNTRACK_NEW NFNLGRP_CONNTRACK_NEW + NFNLGRP_CONNTRACK_UPDATE, +#define NFNLGRP_CONNTRACK_UPDATE NFNLGRP_CONNTRACK_UPDATE + NFNLGRP_CONNTRACK_DESTROY, +#define NFNLGRP_CONNTRACK_DESTROY NFNLGRP_CONNTRACK_DESTROY + NFNLGRP_CONNTRACK_EXP_NEW, +#define NFNLGRP_CONNTRACK_EXP_NEW NFNLGRP_CONNTRACK_EXP_NEW + NFNLGRP_CONNTRACK_EXP_UPDATE, +#define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE + NFNLGRP_CONNTRACK_EXP_DESTROY, +#define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY + NFNLGRP_NFTABLES, +#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES + __NFNLGRP_MAX, +}; +#define NFNLGRP_MAX (__NFNLGRP_MAX - 1) + +/* General form of address family dependent message. + */ +struct nfgenmsg { + __u8 nfgen_family; /* AF_xxx */ + __u8 version; /* nfnetlink version */ + __be16 res_id; /* resource id */ +}; + +#define NFNETLINK_V0 0 + +/* netfilter netlink message types are split in two pieces: + * 8 bit subsystem, 8bit operation. + */ + +#define NFNL_SUBSYS_ID(x) ((x & 0xff00) >> 8) +#define NFNL_MSG_TYPE(x) (x & 0x00ff) + +/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS() + * won't work anymore */ +#define NFNL_SUBSYS_NONE 0 +#define NFNL_SUBSYS_CTNETLINK 1 +#define NFNL_SUBSYS_CTNETLINK_EXP 2 +#define NFNL_SUBSYS_QUEUE 3 +#define NFNL_SUBSYS_ULOG 4 +#define NFNL_SUBSYS_OSF 5 +#define NFNL_SUBSYS_IPSET 6 +#define NFNL_SUBSYS_ACCT 7 +#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 +#define NFNL_SUBSYS_CTHELPER 9 +#define NFNL_SUBSYS_NFTABLES 10 +#define NFNL_SUBSYS_NFT_COMPAT 11 +#define NFNL_SUBSYS_COUNT 12 + +#ifdef __KERNEL__ + +#include +#include +#include + +struct nfnl_callback { + int (*call)(struct sock *nl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]); + int (*call_rcu)(struct sock *nl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]); + const struct nla_policy *policy; /* netlink attribute policy */ + const u_int16_t attr_count; /* number of nlattr's */ +}; + +struct nfnetlink_subsystem { + const char *name; + __u8 subsys_id; /* nfnetlink subsystem ID */ + __u8 cb_count; /* number of callbacks */ + const struct nfnl_callback *cb; /* callback for individual types */ +}; + +extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); +extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); + +extern int nfnetlink_has_listeners(struct net *net, unsigned int group); +extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, + int echo, gfp_t flags); +extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); +extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); + +extern void nfnl_lock(void); +extern void nfnl_unlock(void); + +#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ + MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) + +#endif /* __KERNEL__ */ +#endif /* _NFNETLINK_H */ diff --git a/include/xtables.h b/include/xtables.h index 02172670..bad11a89 100644 --- a/include/xtables.h +++ b/include/xtables.h @@ -401,6 +401,7 @@ struct xtables_globals struct option *orig_opts; struct option *opts; void (*exit_err)(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); + int (*compat_rev)(const char *name, uint8_t rev, int opt); }; #define XT_GETOPT_TABLEEND {.name = NULL, .has_arg = false} @@ -432,6 +433,8 @@ extern struct xtables_match *xtables_find_match(const char *name, enum xtables_tryload, struct xtables_rule_match **match); extern struct xtables_target *xtables_find_target(const char *name, enum xtables_tryload); +extern int xtables_compatible_revision(const char *name, uint8_t revision, + int opt); extern void xtables_rule_matches_free(struct xtables_rule_match **matches); diff --git a/iptables/Makefile.am b/iptables/Makefile.am index a4246eb3..2b1f3fa4 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -1,7 +1,8 @@ # -*- Makefile -*- AM_CFLAGS = ${regular_CFLAGS} -AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS} +AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS} ${libmnl_CPPFLAGS} ${libnftables_CPPFLAGS} +AM_YFLAGS = -d xtables_multi_SOURCES = xtables-multi.c iptables-xml.c xtables_multi_CFLAGS = ${AM_CFLAGS} @@ -24,11 +25,27 @@ endif xtables_multi_SOURCES += xshared.c xtables_multi_LDADD += ../libxtables/libxtables.la -lm +if ENABLE_NFTABLES +if HAVE_LIBMNL +if HAVE_LIBNFTABLES +xtables_multi_SOURCES += xtables-save.c xtables-restore.c \ + xtables-standalone.c xtables.c nft.c \ + xtables-config-parser.y xtables-config-syntax.l \ + xtables-config.c +xtables_multi_LDADD += -lmnl -lnftables +xtables_multi_CFLAGS += -DENABLE_NFTABLES +# yacc and lex generate dirty code +xtables_multi-xtables-config-parser.o xtables_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls +endif +endif +endif + sbin_PROGRAMS = xtables-multi man_MANS = iptables.8 iptables-restore.8 iptables-save.8 \ iptables-xml.1 ip6tables.8 ip6tables-restore.8 \ ip6tables-save.8 iptables-extensions.8 -CLEANFILES = iptables.8 +CLEANFILES = iptables.8 \ + xtables-config-parser.c xtables-config-syntax.c vx_bin_links = iptables-xml if ENABLE_IPV4 @@ -37,6 +54,9 @@ endif if ENABLE_IPV6 v6_sbin_links = ip6tables ip6tables-restore ip6tables-save endif +if ENABLE_NFTABLES +x_sbin_links = xtables xtables-restore xtables-save xtables-config +endif iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man ${AM_VERBOSE_GEN} sed \ @@ -52,3 +72,4 @@ install-exec-hook: for i in ${vx_bin_links}; do ${LN_S} -f "${sbindir}/xtables-multi" "${DESTDIR}${bindir}/$$i"; done; for i in ${v4_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done; for i in ${v6_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done; + for i in ${x_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done; diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c index a5199d5e..2ebfd6c0 100644 --- a/iptables/ip6tables.c +++ b/iptables/ip6tables.c @@ -121,6 +121,7 @@ struct xtables_globals ip6tables_globals = { .program_version = IPTABLES_VERSION, .orig_opts = original_opts, .exit_err = ip6tables_exit_error, + .compat_rev = xtables_compatible_revision, }; /* Table of legal combinations of commands and options. If any of the diff --git a/iptables/iptables.c b/iptables/iptables.c index 5cd2596e..471bff06 100644 --- a/iptables/iptables.c +++ b/iptables/iptables.c @@ -120,6 +120,7 @@ struct xtables_globals iptables_globals = { .program_version = IPTABLES_VERSION, .orig_opts = original_opts, .exit_err = iptables_exit_error, + .compat_rev = xtables_compatible_revision, }; /* Table of legal combinations of commands and options. If any of the diff --git a/iptables/nft.c b/iptables/nft.c new file mode 100644 index 00000000..91383bfb --- /dev/null +++ b/iptables/nft.c @@ -0,0 +1,2764 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#if 0 +#define DEBUGP(x, args...) fprintf(stdout, x, ## args) +#define NLDEBUG +#define DEBUG_DEL +#else +#define DEBUGP(x, args...) +#endif + +#include +#include +#include +#include +#include +#include +#include /* getprotobynumber */ +#include + +#include +#include +#include + +#include +#include + +#include +#include /* FIXME: only IPV4 by now */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include /* inet_ntoa */ +#include + +#include "nft.h" +#include "xshared.h" /* proto_to_name */ + +static void *nft_fn; + +static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, + int (*cb)(const struct nlmsghdr *nlh, void *data), + void *data) +{ + int ret; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + return -1; + } + + ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, h->seq, h->portid, cb, data); + if (ret <= 0) + break; + + ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf)); + } + if (ret == -1) { + return -1; + } + + return 0; +} + +static void nft_table_init_one(struct nft_handle *h, const char *name, int portid) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_table *t; + + t = nft_table_alloc(); + if (t == NULL) + return; + + nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)name); + + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + NLM_F_ACK|NLM_F_EXCL, h->seq); + nft_table_nlmsg_build_payload(nlh, t); + nft_table_free(t); + + if (mnl_talk(h, nlh, NULL, NULL) < 0) { + if (errno != EEXIST) + perror("mnl-talk:nft_table_init_one"); + } +} + +#define FILTER 0 +#define MANGLE 1 +#define RAW 2 +#define SECURITY 3 +#define TABLES_MAX 4 + +static struct default_table { + const char *name; + uint32_t prio; +} tables[TABLES_MAX] = { + [RAW] = { + .name = "raw", + .prio = -300, /* NF_IP_PRI_RAW */ + }, + [MANGLE] = { + .name = "mangle", + .prio = -150, /* NF_IP_PRI_MANGLE */ + }, + [FILTER] = { + .name = "filter", + .prio = 0, /* NF_IP_PRI_FILTER */ + }, + [SECURITY] = { + .name = "security", + .prio = 150, /* NF_IP_PRI_SECURITY */ + }, + /* nat already registered by nf_tables */ +}; + +static void nft_table_init(struct nft_handle *h) +{ + int i; + + for (i=0; iportid); +} + +static struct default_chain { + const char *name; + uint32_t hook; +} chains[TABLES_MAX][NF_IP_NUMHOOKS] = { + [FILTER] = { + { + .name = "INPUT", + .hook = NF_INET_LOCAL_IN, + }, + { + .name = "FORWARD", + .hook = NF_INET_FORWARD, + }, + { + .name = "OUTPUT", + .hook = NF_INET_LOCAL_OUT, + }, + }, + [MANGLE] = { + { + .name = "PREROUTING", + .hook = NF_INET_PRE_ROUTING, + }, + { + .name = "INPUT", + .hook = NF_INET_LOCAL_IN, + }, + { + .name = "FORWARD", + .hook = NF_INET_FORWARD, + }, + { + .name = "OUTPUT", + .hook = NF_INET_LOCAL_OUT, + }, + { + .name = "POSTROUTING", + .hook = NF_INET_POST_ROUTING, + }, + }, + [RAW] = { + { + .name = "PREROUTING", + .hook = NF_INET_PRE_ROUTING, + }, + { + .name = "OUTPUT", + .hook = NF_INET_LOCAL_OUT, + }, + }, + [SECURITY] = { + { + .name = "INPUT", + .hook = NF_INET_LOCAL_IN, + }, + { + .name = "FORWARD", + .hook = NF_INET_FORWARD, + }, + { + .name = "OUTPUT", + .hook = NF_INET_LOCAL_OUT, + }, + }, +}; + +static void +nft_chain_default_add(struct nft_handle *h, struct default_table *table, + struct default_chain *chain, int policy) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_chain *c; + + c = nft_chain_alloc(); + if (c == NULL) + return; + + nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table->name); + nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain->name); + nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_HOOKNUM, chain->hook); + nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_PRIO, table->prio); + nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy); + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + NLM_F_ACK|NLM_F_EXCL, h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + nft_chain_free(c); + + if (mnl_talk(h, nlh, NULL, NULL) < 0) { + if (errno != EEXIST) + perror("mnl_talk:nft_chain_default_add"); + } +} + +static void nft_chain_init(struct nft_handle *h) +{ + int i, j; + + for (i=0; inl = mnl_socket_open(NETLINK_NETFILTER); + if (h->nl == NULL) { + perror("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + return -1; + } + h->portid = mnl_socket_get_portid(h->nl); + +/* + * In case we want to inconditionally register all tables / chain. + * This is not flexible and performance consuming. + * + * nft_table_init(h); + * nft_chain_init(h); + */ + + return 0; +} + +void nft_fini(struct nft_handle *h) +{ + mnl_socket_close(h->nl); +} + +int nft_table_add(struct nft_handle *h, const struct nft_table *t) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + NLM_F_ACK|NLM_F_EXCL, h->seq); + nft_table_nlmsg_build_payload(nlh, t); + + return mnl_talk(h, nlh, NULL, NULL); +} + +int nft_chain_add(struct nft_handle *h, const struct nft_chain *c) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + NLM_F_ACK|NLM_F_EXCL, h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + + return mnl_talk(h, nlh, NULL, NULL); +} + +static void nft_chain_print_debug(struct nft_chain *c, struct nlmsghdr *nlh) +{ +#ifdef NLDEBUG + char tmp[1024]; + + nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0); + printf("DEBUG: chain: %s", tmp); + mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg)); +#endif +} + +static int +__nft_chain_set(struct nft_handle *h, const char *table, + const char *chain, int policy, + const struct xt_counters *counters) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_chain *c; + int ret; + + c = nft_chain_alloc(); + if (c == NULL) + return -1; + + nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); + nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); + nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy); + if (counters) { + nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_BYTES, + counters->bcnt); + nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_PACKETS, + counters->pcnt); + } + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + NLM_F_ACK, h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + + nft_chain_print_debug(c, nlh); + + nft_chain_free(c); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) + perror("mnl_talk:__nft_chain_policy"); + + return ret; +} + +int nft_chain_set(struct nft_handle *h, const char *table, + const char *chain, const char *policy, + const struct xt_counters *counters) +{ + int ret = -1; + + nft_fn = nft_chain_set; + + if (strcmp(policy, "DROP") == 0) + ret = __nft_chain_set(h, table, chain, NF_DROP, counters); + else if (strcmp(policy, "ACCEPT") == 0) + ret = __nft_chain_set(h, table, chain, NF_ACCEPT, counters); + + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +static void __add_match(struct nft_rule_expr *e, struct xt_entry_match *m) +{ + void *info; + + nft_rule_expr_set(e, NFT_EXPR_MT_NAME, m->u.user.name, strlen(m->u.user.name)); + nft_rule_expr_set_u32(e, NFT_EXPR_MT_REV, m->u.user.revision); + + info = calloc(1, m->u.match_size); + if (info == NULL) + return; + + memcpy(info, m->data, m->u.match_size); + nft_rule_expr_set(e, NFT_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m)); +} + +static void add_match(struct nft_rule *r, struct xt_entry_match *m) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("match"); + if (expr == NULL) + return; + + __add_match(expr, m); + nft_rule_add_expr(r, expr); +} + +static void __add_target(struct nft_rule_expr *e, struct xt_entry_target *t) +{ + void *info = NULL; + + nft_rule_expr_set(e, NFT_EXPR_TG_NAME, t->u.user.name, + strlen(t->u.user.name)); + nft_rule_expr_set_u32(e, NFT_EXPR_TG_REV, t->u.user.revision); + + if (info == NULL) { + info = calloc(1, t->u.target_size); + if (info == NULL) + return; + + memcpy(info, t->data, t->u.target_size); + } + + nft_rule_expr_set(e, NFT_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t)); +} + +static void add_target(struct nft_rule *r, struct xt_entry_target *t) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("target"); + if (expr == NULL) + return; + + __add_target(expr, t); + nft_rule_add_expr(r, expr); +} + +static void add_jumpto(struct nft_rule *r, const char *name, int verdict) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("immediate"); + if (expr == NULL) + return; + + nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT); + nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_VERDICT, verdict); + nft_rule_expr_set_str(expr, NFT_EXPR_IMM_CHAIN, (char *)name); + nft_rule_add_expr(r, expr); +} + +static void add_verdict(struct nft_rule *r, int verdict) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("immediate"); + if (expr == NULL) + return; + + nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT); + nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_VERDICT, verdict); + nft_rule_add_expr(r, expr); +} + +static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) +{ +#ifdef NLDEBUG + char tmp[1024]; + + nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0); + printf("DEBUG: rule: %s", tmp); + mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg)); +#endif +} + +static void add_meta(struct nft_rule *r, uint32_t key) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("meta"); + if (expr == NULL) + return; + + nft_rule_expr_set_u32(expr, NFT_EXPR_META_KEY, key); + nft_rule_expr_set_u32(expr, NFT_EXPR_META_DREG, NFT_REG_1); + + nft_rule_add_expr(r, expr); +} + +static void add_payload(struct nft_rule *r, int offset, int len) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("payload"); + if (expr == NULL) + return; + + nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_BASE, + NFT_PAYLOAD_NETWORK_HEADER); + nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_DREG, NFT_REG_1); + nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_OFFSET, offset); + nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_LEN, len); + + nft_rule_add_expr(r, expr); +} + +static void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("cmp"); + if (expr == NULL) + return; + + nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_SREG, NFT_REG_1); + nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_OP, op); + nft_rule_expr_set(expr, NFT_EXPR_CMP_DATA, data, len); + + nft_rule_add_expr(r, expr); +} + +static void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op) +{ + add_cmp_ptr(r, op, &val, sizeof(val)); +} + +static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("counter"); + if (expr == NULL) + return; + + nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_BYTES, packets); + nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_PACKETS, bytes); + + nft_rule_add_expr(r, expr); +} + +int +nft_rule_add(struct nft_handle *h, const char *chain, const char *table, + struct iptables_command_state *cs, bool append, bool verbose) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct xtables_rule_match *matchp; + struct nft_rule *r; + int ret = 1; + uint32_t op; + int flags = append ? NLM_F_APPEND : 0; + + nft_fn = nft_rule_add; + + r = nft_rule_alloc(); + if (r == NULL) { + ret = 0; + goto err; + } + + nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); + nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); + + if (cs->fw.ip.iniface[0] != '\0') { + int iface_len = strlen(cs->fw.ip.iniface); + + if (cs->fw.ip.invflags & IPT_INV_VIA_IN) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + if (cs->fw.ip.iniface[iface_len - 1] == '+') { + add_meta(r, NFT_META_IIFNAME); + add_cmp_ptr(r, op, cs->fw.ip.iniface, iface_len - 1); + } else { + add_meta(r, NFT_META_IIF); + add_cmp_u32(r, if_nametoindex(cs->fw.ip.iniface), op); + } + } + if (cs->fw.ip.outiface[0] != '\0') { + int iface_len = strlen(cs->fw.ip.outiface); + + if (cs->fw.ip.invflags & IPT_INV_VIA_OUT) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + if (cs->fw.ip.outiface[iface_len - 1] == '+') { + add_meta(r, NFT_META_OIFNAME); + add_cmp_ptr(r, op, cs->fw.ip.outiface, iface_len - 1); + } else { + add_meta(r, NFT_META_OIF); + add_cmp_u32(r, if_nametoindex(cs->fw.ip.outiface), op); + } + } + if (cs->fw.ip.src.s_addr != 0) { + add_payload(r, offsetof(struct iphdr, saddr), 4); + if (cs->fw.ip.invflags & IPT_INV_SRCIP) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_u32(r, cs->fw.ip.src.s_addr, op); + } + if (cs->fw.ip.dst.s_addr != 0) { + add_payload(r, offsetof(struct iphdr, daddr), 4); + if (cs->fw.ip.invflags & IPT_INV_DSTIP) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_u32(r, cs->fw.ip.dst.s_addr, op); + } + if (cs->fw.ip.proto != 0) { + add_payload(r, offsetof(struct iphdr, protocol), 1); + if (cs->fw.ip.invflags & XT_INV_PROTO) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_u32(r, cs->fw.ip.proto, op); + } + + for (matchp = cs->matches; matchp; matchp = matchp->next) + add_match(r, matchp->match->m); + + /* Counters need to me added before the target, otherwise they are + * increased for each rule because of the way nf_tables works. + */ + add_counters(r, cs->counters.pcnt, cs->counters.bcnt); + + /* If no target at all, add nothing (default to continue) */ + if (cs->target != NULL) { + /* Standard target? */ + if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0) + add_verdict(r, NF_ACCEPT); + else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) + add_verdict(r, NF_DROP); + else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0) + add_verdict(r, NFT_RETURN); + else + add_target(r, cs->target->t); + } else if (strlen(cs->jumpto) > 0) { + /* Not standard, then it's a go / jump to chain */ + if (cs->fw.ip.flags & IPT_F_GOTO) + add_jumpto(r, cs->jumpto, NFT_GOTO); + else + add_jumpto(r, cs->jumpto, NFT_JUMP); + } + + /* NLM_F_CREATE autoloads the built-in table if it does not exists */ + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, AF_INET, + NLM_F_ACK|NLM_F_CREATE|flags, h->seq); + nft_rule_nlmsg_build_payload(nlh, r); + + nft_rule_print_debug(r, nlh); + + nft_rule_free(r); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) + perror("mnl_talk:nft_rule_add"); + +err: + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +static void nft_match_save(struct nft_rule_expr *expr) +{ + const char *name; + const struct xtables_match *match; + struct xt_entry_match *emu; + const void *mtinfo; + size_t len; + + name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); + + match = xtables_find_match(name, XTF_TRY_LOAD, NULL); + if (match == NULL) + return; + + mtinfo = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len); + if (mtinfo == NULL) + return; + + emu = calloc(1, sizeof(struct xt_entry_match) + len); + if (emu == NULL) + return; + + memcpy(&emu->data, mtinfo, len); + + if (match->alias) + printf("-m %s", match->alias(emu)); + else + printf("-m %s", match->name); + + /* FIXME missing parameter */ + match->save(NULL, emu); + + printf(" "); + + free(emu); +} + +static void nft_target_save(struct nft_rule_expr *expr) +{ + const char *name; + const struct xtables_target *target; + struct xt_entry_target *emu; + const void *tginfo; + size_t len; + + name = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME); + + /* Standard target not supported, we use native immediate expression */ + if (strcmp(name, "") == 0) { + printf("ERROR: standard target seen, should not happen\n"); + return; + } + + target = xtables_find_target(name, XTF_TRY_LOAD); + if (target == NULL) + return; + + tginfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &len); + if (tginfo == NULL) + return; + + emu = calloc(1, sizeof(struct xt_entry_match) + len); + if (emu == NULL) + return; + + memcpy(emu->data, tginfo, len); + + if (target->alias) + printf("-j %s", target->alias(emu)); + else + printf("-j %s", target->name); + + /* FIXME missing parameter */ + target->save(NULL, emu); + + free(emu); +} + +static void nft_immediate_save(struct nft_rule_expr *expr) +{ + uint32_t verdict; + + verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); + + switch(verdict) { + case NF_ACCEPT: + printf("-j ACCEPT"); + break; + case NF_DROP: + printf("-j DROP"); + break; + case NFT_RETURN: + printf("-j RETURN"); + break; + case NFT_GOTO: + printf("-g %s", + nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN)); + break; + case NFT_JUMP: + printf("-j %s", + nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN)); + break; + } +} + +static void +nft_print_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) +{ + uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); + uint32_t value; + const char *name; + char ifname[IFNAMSIZ]; + const char *ifname_ptr; + size_t len; + + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + /* meta should be followed by cmp */ + if (strcmp(name, "cmp") != 0) { + DEBUGP("skipping no cmp after meta\n"); + return; + } + + switch(key) { + case NFT_META_IIF: + value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); + if_indextoname(value, ifname); + + switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) { + case NFT_CMP_EQ: + printf("-i %s ", ifname); + break; + case NFT_CMP_NEQ: + printf("! -i %s ", ifname); + break; + } + break; + case NFT_META_OIF: + value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); + if_indextoname(value, ifname); + + switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) { + case NFT_CMP_EQ: + printf("-o %s ", ifname); + break; + case NFT_CMP_NEQ: + printf("! -o %s ", ifname); + break; + } + break; + case NFT_META_IIFNAME: + ifname_ptr = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); + memcpy(ifname, ifname_ptr, len); + ifname[len] = '\0'; + + /* if this is zero, then assume this is a interface mask */ + if (if_nametoindex(ifname) == 0) { + ifname[len] = '+'; + ifname[len+1] = '\0'; + } + + switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) { + case NFT_CMP_EQ: + printf("-i %s ", ifname); + break; + case NFT_CMP_NEQ: + printf("! -i %s ", ifname); + break; + } + break; + case NFT_META_OIFNAME: + ifname_ptr = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); + memcpy(ifname, ifname_ptr, len); + ifname[len] = '\0'; + + /* if this is zero, then assume this is a interface mask */ + if (if_nametoindex(ifname) == 0) { + ifname[len] = '+'; + ifname[len+1] = '\0'; + } + + switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) { + case NFT_CMP_EQ: + printf("-o %s ", ifname); + break; + case NFT_CMP_NEQ: + printf("! -o %s ", ifname); + break; + } + break; + default: + DEBUGP("unknown meta key %d\n", key); + break; + } +} + +static void +get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv) +{ + struct nft_rule_expr *e; + const char *name; + size_t len; + uint8_t op; + + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + if (strcmp(name, "cmp") != 0) { + DEBUGP("skipping no cmp after meta\n"); + return; + } + + memcpy(data, nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len), dlen); + op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); + if (op == NFT_CMP_NEQ) + *inv = true; + else + *inv = false; +} + +static void print_proto(uint16_t proto, int invert) +{ + const struct protoent *pent = getprotobynumber(proto); + + if (invert) + printf("! "); + + if (pent) { + printf("-p %s ", pent->p_name); + return; + } + + printf("-p %u ", proto); +} + +static const char *mask_to_str(uint32_t mask) +{ + static char mask_str[sizeof("255.255.255.255")]; + uint32_t bits, hmask = ntohl(mask); + struct in_addr mask_addr = { + .s_addr = mask, + }; + int i; + + if (mask == 0xFFFFFFFFU) { + sprintf(mask_str, "32"); + return mask_str; + } + + i = 32; + bits = 0xFFFFFFFEU; + while (--i >= 0 && hmask != bits) + bits <<= 1; + if (i >= 0) + sprintf(mask_str, "%u", i); + else + sprintf(mask_str, "%s", inet_ntoa(mask_addr)); + + return mask_str; +} + +static void +nft_print_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) +{ + uint32_t offset; + bool inv; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + switch(offset) { + struct in_addr addr; + uint8_t proto; + + case offsetof(struct iphdr, saddr): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + if (inv) + printf("! -s %s/%s ", inet_ntoa(addr), + mask_to_str(0xffffffff)); + else + printf("-s %s/%s ", inet_ntoa(addr), + mask_to_str(0xffffffff)); + break; + case offsetof(struct iphdr, daddr): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + if (inv) + printf("! -d %s/%s ", inet_ntoa(addr), + mask_to_str(0xffffffff)); + else + printf("-d %s/%s ", inet_ntoa(addr), + mask_to_str(0xffffffff)); + break; + case offsetof(struct iphdr, protocol): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + print_proto(proto, inv); + break; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + +static void +nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + bool counters) +{ + if (counters) { + printf("-c %lu %lu ", + nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS), + nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES)); + } +} + +static void nft_rule_print_save(struct nft_rule *r, bool counters) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + + /* print chain name */ + printf("-A %s ", nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN)); + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) + return; + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "counter") == 0) { + nft_print_counters(expr, iter, counters); + } else if (strcmp(name, "payload") == 0) { + nft_print_payload(expr, iter); + } else if (strcmp(name, "meta") == 0) { + nft_print_meta(expr, iter); + } else if (strcmp(name, "match") == 0) { + nft_match_save(expr); + } else if (strcmp(name, "target") == 0) { + nft_target_save(expr); + } else if (strcmp(name, "immediate") == 0) { + nft_immediate_save(expr); + } + + expr = nft_rule_expr_iter_next(iter); + } + + printf("\n"); +} + +static int nft_chain_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_chain *c; + struct nft_chain_list *list = data; + + c = nft_chain_alloc(); + if (c == NULL) { + perror("OOM"); + goto err; + } + + if (nft_chain_nlmsg_parse(nlh, c) < 0) { + perror("nft_rule_nlmsg_parse"); + goto out; + } + + nft_chain_list_add(c, list); + + return MNL_CB_OK; +out: + nft_chain_free(c); +err: + return MNL_CB_OK; +} + +static struct nft_chain_list *nft_chain_list_get(struct nft_handle *h) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int ret; + struct nft_chain_list *list; + + list = nft_chain_list_alloc(); + if (list == NULL) { + DEBUGP("cannot allocate rule list\n"); + return 0; + } + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, AF_INET, + NLM_F_DUMP, h->seq); + + ret = mnl_talk(h, nlh, nft_chain_list_cb, list); + if (ret < 0) + perror("mnl_talk:nft_chain_list_get"); + + return list; +} + +struct nft_chain_list *nft_chain_dump(struct nft_handle *h) +{ + return nft_chain_list_get(h); +} + +static const char *policy_name[NF_ACCEPT+1] = { + [NF_DROP] = "DROP", + [NF_ACCEPT] = "ACCEPT", +}; + +static void nft_chain_print_save(struct nft_chain *c, bool basechain) +{ + const char *chain = nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); + uint64_t pkts = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS); + uint64_t bytes = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES); + + /* print chain name */ + if (basechain) { + uint32_t pol = NF_ACCEPT; + + /* no default chain policy? don't crash, display accept */ + if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_POLICY)) + pol = nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY); + + printf(":%s %s [%lu:%lu]\n", chain, policy_name[pol], + pkts, bytes); + } else + printf(":%s - [%lu:%lu]\n", chain, pkts, bytes); +} + +int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, + const char *table) +{ + struct nft_chain_list_iter *iter; + struct nft_chain *c; + + iter = nft_chain_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + c = nft_chain_list_iter_next(iter); + while (c != NULL) { + const char *chain_table = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE); + bool basechain = false; + + if (strcmp(table, chain_table) != 0) + goto next; + + if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) + basechain = true; + + nft_chain_print_save(c, basechain); +next: + c = nft_chain_list_iter_next(iter); + } + + nft_chain_list_free(list); + + return 1; +} + +static int nft_rule_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_rule *r; + struct nft_rule_list *list = data; + + r = nft_rule_alloc(); + if (r == NULL) { + perror("OOM"); + goto err; + } + + if (nft_rule_nlmsg_parse(nlh, r) < 0) { + perror("nft_rule_nlmsg_parse"); + goto out; + } + + nft_rule_list_add(r, list); + + return MNL_CB_OK; +out: + nft_rule_free(r); +err: + return MNL_CB_OK; +} + +static struct nft_rule_list *nft_rule_list_get(struct nft_handle *h) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_rule_list *list; + int ret; + + list = nft_rule_list_alloc(); + if (list == NULL) { + DEBUGP("cannot allocate rule list\n"); + return 0; + } + + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, AF_INET, + NLM_F_DUMP, h->seq); + + ret = mnl_talk(h, nlh, nft_rule_list_cb, list); + if (ret < 0) { + perror("mnl_talk:nft_rule_save"); + nft_rule_list_free(list); + return NULL; + } + + return list; +} + +int nft_rule_save(struct nft_handle *h, const char *table, bool counters) +{ + struct nft_rule_list *list; + struct nft_rule_list_iter *iter; + struct nft_rule *r; + + list = nft_rule_list_get(h); + if (list == NULL) { + DEBUGP("cannot retrieve rule list from kernel\n"); + return 0; + } + + iter = nft_rule_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + r = nft_rule_list_iter_next(iter); + while (r != NULL) { + const char *rule_table = + nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE); + + if (strcmp(table, rule_table) != 0) + goto next; + + nft_rule_print_save(r, counters); + +next: + r = nft_rule_list_iter_next(iter); + } + + nft_rule_list_free(list); + + /* the core expects 1 for success and 0 for error */ + return 1; +} + +static void +__nft_rule_flush(struct nft_handle *h, const char *table, const char *chain) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_rule *r; + + r = nft_rule_alloc(); + if (r == NULL) + return; + + nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); + nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); + + /* Delete all rules in this table + chain */ + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, AF_INET, + NLM_F_ACK, h->seq); + nft_rule_nlmsg_build_payload(nlh, r); + nft_rule_free(r); + + if (mnl_talk(h, nlh, NULL, NULL) < 0) { + if (errno != EEXIST) + perror("mnl_talk:__nft_rule_flush"); + } +} + +int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table) +{ + int ret; + struct nft_chain_list *list; + struct nft_chain_list_iter *iter; + struct nft_chain *c; + + nft_fn = nft_rule_flush; + + list = nft_chain_list_get(h); + if (list == NULL) { + ret = 0; + goto err; + } + + iter = nft_chain_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + c = nft_chain_list_iter_next(iter); + while (c != NULL) { + const char *table_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE); + const char *chain_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); + + if (strcmp(table, table_name) != 0) + goto next; + + if (chain != NULL && strcmp(chain, chain_name) != 0) + goto next; + + __nft_rule_flush(h, table_name, chain_name); + +next: + c = nft_chain_list_iter_next(iter); + } + +err: + nft_chain_list_free(list); + + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_chain *c; + int ret; + + c = nft_chain_alloc(); + if (c == NULL) { + DEBUGP("cannot allocate chain\n"); + return -1; + } + + nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); + nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + NLM_F_ACK|NLM_F_EXCL, h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + nft_chain_free(c); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) { + if (errno != EEXIST) + perror("mnl_talk:nft_chain_add"); + } + + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +static int __nft_chain_del(struct nft_handle *h, struct nft_chain *c) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int ret; + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, AF_INET, + NLM_F_ACK, h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) { + if (errno != EEXIST && errno != ENOENT) + perror("mnl_talk:__nft_chain_del"); + } + + return ret; +} + +static bool nft_chain_builtin(struct nft_chain *c) +{ + /* Check if this chain has hook number, in that case is built-in. + * Should we better export the flags to user-space via nf_tables? + */ + return nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM) != NULL; +} + +int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table) +{ + struct nft_chain_list *list; + struct nft_chain_list_iter *iter; + struct nft_chain *c; + int ret = 0; + int deleted_ctr = 0; + + list = nft_chain_list_get(h); + if (list == NULL) + goto err; + + iter = nft_chain_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + c = nft_chain_list_iter_next(iter); + while (c != NULL) { + const char *table_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE); + const char *chain_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); + + /* don't delete built-in chain */ + if (nft_chain_builtin(c)) + goto next; + + if (strcmp(table, table_name) != 0) + goto next; + + if (chain != NULL && strcmp(chain, chain_name) != 0) + goto next; + + ret = __nft_chain_del(h, c); + if (ret < 0) + break; + + deleted_ctr++; +next: + c = nft_chain_list_iter_next(iter); + } + +err: + nft_chain_list_free(list); + + /* chain not found */ + if (ret < 0 && deleted_ctr == 0) + errno = ENOENT; + + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +int nft_chain_user_rename(struct nft_handle *h,const char *chain, + const char *table, const char *newname) +{ + int ret; + + /* XXX need new operation in nf_tables to support this */ + ret = nft_chain_user_del(h, chain, table); + if (ret < 0) + return ret; + + return nft_chain_user_add(h, newname, table); +} + +static int nft_table_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_table *t; + struct nft_table_list *list = data; + + t = nft_table_alloc(); + if (t == NULL) { + perror("OOM"); + goto err; + } + + if (nft_table_nlmsg_parse(nlh, t) < 0) { + perror("nft_rule_nlmsg_parse"); + goto out; + } + + nft_table_list_add(t, list); + + return MNL_CB_OK; +out: + nft_table_free(t); +err: + return MNL_CB_OK; +} + +static struct nft_table_list *nft_table_list_get(struct nft_handle *h) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int ret; + struct nft_table_list *list; + + list = nft_table_list_alloc(); + if (list == NULL) { + DEBUGP("cannot allocate table list\n"); + return 0; + } + + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, AF_INET, + NLM_F_DUMP, h->seq); + + ret = mnl_talk(h, nlh, nft_table_list_cb, list); + if (ret < 0) + perror("mnl_talk:nft_table_list_get"); + + return list; +} + +bool nft_table_find(struct nft_handle *h, const char *tablename) +{ + struct nft_table_list *list; + struct nft_table_list_iter *iter; + struct nft_table *t; + bool ret = false; + + list = nft_table_list_get(h); + if (list == NULL) + goto err; + + iter = nft_table_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + goto err; + } + + t = nft_table_list_iter_next(iter); + while (t != NULL) { + const char *this_tablename = + nft_table_attr_get(t, NFT_TABLE_ATTR_NAME); + + if (strcmp(tablename, this_tablename) == 0) + return true; + + t = nft_table_list_iter_next(iter); + } + + nft_table_list_free(list); + +err: + return ret; +} + +int nft_for_each_table(struct nft_handle *h, + int (*func)(struct nft_handle *h, const char *tablename, bool counters), + bool counters) +{ + int ret = 1; + struct nft_table_list *list; + struct nft_table_list_iter *iter; + struct nft_table *t; + + list = nft_table_list_get(h); + if (list == NULL) { + ret = 0; + goto err; + } + + iter = nft_table_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + t = nft_table_list_iter_next(iter); + while (t != NULL) { + const char *tablename = + nft_table_attr_get(t, NFT_TABLE_ATTR_NAME); + + func(h, tablename, counters); + + t = nft_table_list_iter_next(iter); + } + + nft_table_list_free(list); + +err: + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +static inline int +match_different(const struct xt_entry_match *a, + const unsigned char *a_elems, + const unsigned char *b_elems, + unsigned char **maskptr) +{ + const struct xt_entry_match *b; + unsigned int i; + + /* Offset of b is the same as a. */ + b = (void *)b_elems + ((unsigned char *)a - a_elems); + + if (a->u.match_size != b->u.match_size) + return 1; + + if (strcmp(a->u.user.name, b->u.user.name) != 0) + return 1; + + *maskptr += XT_ALIGN(sizeof(*a)); + + for (i = 0; i < a->u.match_size - XT_ALIGN(sizeof(*a)); i++) + if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0) + return 1; + *maskptr += i; + return 0; +} + +static bool +is_same(const struct iptables_command_state *a, const struct iptables_command_state *b) +{ + unsigned int i; + + /* Always compare head structures: ignore mask here. */ + if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr + || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr + || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr + || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr + || a->fw.ip.proto != b->fw.ip.proto + || a->fw.ip.flags != b->fw.ip.flags + || a->fw.ip.invflags != b->fw.ip.invflags) { + DEBUGP("different src/dst/proto/flags/invflags\n"); + return false; + } + + for (i = 0; i < IFNAMSIZ; i++) { + if (a->fw.ip.iniface_mask[i] != b->fw.ip.iniface_mask[i]) { + DEBUGP("different iniface mask %x, %x (%d)\n", + a->fw.ip.iniface_mask[i] & 0xff, b->fw.ip.iniface_mask[i] & 0xff, i); + return false; + } + if ((a->fw.ip.iniface[i] & a->fw.ip.iniface_mask[i]) + != (b->fw.ip.iniface[i] & b->fw.ip.iniface_mask[i])) { + DEBUGP("different iniface\n"); + return false; + } + if (a->fw.ip.outiface_mask[i] != b->fw.ip.outiface_mask[i]) { + DEBUGP("different outiface mask\n"); + return false; + } + if ((a->fw.ip.outiface[i] & a->fw.ip.outiface_mask[i]) + != (b->fw.ip.outiface[i] & b->fw.ip.outiface_mask[i])) { + DEBUGP("different outiface\n"); + return false; + } + } + + return true; +} + +static void +nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) +{ + uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); + uint32_t value; + const char *name; + const void *ifname; + size_t len; + + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + if (strcmp(name, "cmp") != 0) { + DEBUGP("skipping no cmp after meta\n"); + return; + } + + switch(key) { + case NFT_META_IIF: + value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); + if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + cs->fw.ip.invflags |= IPT_INV_VIA_IN; + + if_indextoname(value, cs->fw.ip.iniface); + + memset(cs->fw.ip.iniface_mask, 0xff, + strlen(cs->fw.ip.iniface)+1); + break; + case NFT_META_OIF: + value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); + if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + cs->fw.ip.invflags |= IPT_INV_VIA_OUT; + + if_indextoname(value, cs->fw.ip.outiface); + + memset(cs->fw.ip.outiface_mask, 0xff, + strlen(cs->fw.ip.outiface)+1); + break; + case NFT_META_IIFNAME: + ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); + if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + cs->fw.ip.invflags |= IPT_INV_VIA_IN; + + memcpy(cs->fw.ip.iniface, ifname, len); + cs->fw.ip.iniface[len] = '\0'; + + /* If zero, then this is an interface mask */ + if (if_nametoindex(cs->fw.ip.iniface) == 0) { + cs->fw.ip.iniface[len] = '+'; + cs->fw.ip.iniface[len+1] = '\0'; + } + + memset(cs->fw.ip.iniface_mask, 0xff, len); + break; + case NFT_META_OIFNAME: + ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); + if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + cs->fw.ip.invflags |= IPT_INV_VIA_OUT; + + memcpy(cs->fw.ip.outiface, ifname, len); + cs->fw.ip.outiface[len] = '\0'; + + /* If zero, then this is an interface mask */ + if (if_nametoindex(cs->fw.ip.outiface) == 0) { + cs->fw.ip.outiface[len] = '+'; + cs->fw.ip.outiface[len+1] = '\0'; + } + + memset(cs->fw.ip.outiface_mask, 0xff, len); + break; + default: + DEBUGP("unknown meta key %d\n", key); + break; + } +} + +static void +nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) +{ + uint32_t offset; + bool inv; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + switch(offset) { + struct in_addr addr; + uint8_t proto; + + case offsetof(struct iphdr, saddr): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + cs->fw.ip.src.s_addr = addr.s_addr; + cs->fw.ip.smsk.s_addr = 0xffffffff; + if (inv) + cs->fw.ip.invflags |= IPT_INV_SRCIP; + break; + case offsetof(struct iphdr, daddr): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + cs->fw.ip.dst.s_addr = addr.s_addr; + cs->fw.ip.dmsk.s_addr = 0xffffffff; + if (inv) + cs->fw.ip.invflags |= IPT_INV_DSTIP; + break; + case offsetof(struct iphdr, protocol): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + cs->fw.ip.proto = proto; + if (inv) + cs->fw.ip.invflags |= IPT_INV_PROTO; + break; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + +static void +nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + struct xt_counters *counters) +{ + counters->pcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS); + counters->bcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES); +} + +static void +nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) +{ + int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); + const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); + + /* Standard target? */ + switch(verdict) { + case NF_ACCEPT: + cs->jumpto = "ACCEPT"; + return; + case NF_DROP: + cs->jumpto = "DROP"; + return; + case NFT_RETURN: + cs->jumpto = "RETURN"; + return; + case NFT_GOTO: + cs->fw.ip.flags |= IPT_F_GOTO; + case NFT_JUMP: + cs->jumpto = chain; + return; + } +} + +static void +nft_rule_to_iptables_command_state(struct nft_rule *r, + struct iptables_command_state *cs) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) + return; + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "counter") == 0) { + nft_parse_counter(expr, iter, &cs->counters); + } else if (strcmp(name, "payload") == 0) { + nft_parse_payload(expr, iter, cs); + } else if (strcmp(name, "meta") == 0) { + nft_parse_meta(expr, iter, cs); + } else if (strcmp(name, "immediate") == 0) { + nft_parse_immediate(expr, iter, cs); + } + + expr = nft_rule_expr_iter_next(iter); + } + + nft_rule_expr_iter_destroy(iter); +} + +static int matches_howmany(struct xtables_rule_match *matches) +{ + struct xtables_rule_match *matchp; + int matches_ctr = 0; + + for (matchp = matches; matchp; matchp = matchp->next) + matches_ctr++; + + return matches_ctr; +} + +static bool +__find_match(struct nft_rule_expr *expr, struct xtables_rule_match *matches) +{ + const char *matchname = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); + /* Netlink aligns this match info, don't trust this length variable */ + const char *data = nft_rule_expr_get_str(expr, NFT_EXPR_MT_INFO); + struct xtables_rule_match *matchp; + bool found = false; + + for (matchp = matches; matchp; matchp = matchp->next) { + struct xt_entry_match *m = matchp->match->m; + + if (strcmp(m->u.user.name, matchname) != 0) { + DEBUGP("mismatching match name\n"); + continue; + } + + if (memcmp(data, m->data, m->u.user.match_size - sizeof(*m)) != 0) { + DEBUGP("mismatch match data\n"); + continue; + } + found = true; + break; + } + + return found; +} + +static bool find_matches(struct xtables_rule_match *matches, struct nft_rule *r) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + int kernel_matches = 0; + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return false; + } + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "match") == 0) { + if (!__find_match(expr, matches)) + return false; + + kernel_matches++; + } + expr = nft_rule_expr_iter_next(iter); + } + nft_rule_expr_iter_destroy(iter); + + /* same number of matches? */ + if (matches_howmany(matches) != kernel_matches) + return false; + + return true; +} + +static bool __find_target(struct nft_rule_expr *expr, struct xt_entry_target *t) +{ + size_t len; + const char *tgname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME); + /* Netlink aligns this target info, don't trust this length variable */ + const char *data = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &len); + + if (strcmp(t->u.user.name, tgname) != 0) { + DEBUGP("mismatching target name\n"); + return false; + } + + if (memcmp(data, t->data, t->u.user.target_size - sizeof(*t)) != 0) + return false; + + return true; +} + +static int targets_howmany(struct xtables_target *target) +{ + return target != NULL ? 1 : 0; +} + +static bool +find_target(struct xtables_target *target, struct nft_rule *r) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + int kernel_targets = 0; + + /* Special case: we use native immediate expressions to emulated + * standard targets. Also, we don't want to crash with no targets. + */ + if (target == NULL || strcmp(target->name, "standard") == 0) + return true; + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return false; + } + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "target") == 0) { + /* we may support several targets in the future */ + if (!__find_target(expr, target->t)) + return false; + + kernel_targets++; + } + expr = nft_rule_expr_iter_next(iter); + } + nft_rule_expr_iter_destroy(iter); + + /* same number of targets? */ + if (targets_howmany(target) != kernel_targets) { + DEBUGP("kernel targets is %d but we passed %d\n", + kernel_targets, targets_howmany(target)); + return false; + } + + return true; +} + +static bool +find_immediate(struct nft_rule *r, const char *jumpto) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return false; + } + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "immediate") == 0) { + int verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); + const char *verdict_name = NULL; + + /* No target specified but immediate shows up, this + * is not the rule we are looking for. + */ + if (strlen(jumpto) == 0) + return false; + + switch(verdict) { + case NF_ACCEPT: + verdict_name = "ACCEPT"; + break; + case NF_DROP: + verdict_name = "DROP"; + break; + case NFT_RETURN: + verdict_name = "RETURN"; + break; + } + + /* Standard target? */ + if (verdict_name && strcmp(jumpto, verdict_name) != 0) + return false; + } + expr = nft_rule_expr_iter_next(iter); + } + nft_rule_expr_iter_destroy(iter); + + return true; +} + +static void +__nft_rule_del(struct nft_handle *h, struct nft_rule *r) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int ret; + + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, AF_INET, + NLM_F_ACK, h->seq); + nft_rule_nlmsg_build_payload(nlh, r); + + nft_rule_print_debug(r, nlh); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) + perror("mnl_talk:nft_rule_del"); +} + +static int +__nft_rule_check(struct nft_handle *h, const char *chain, const char *table, + struct iptables_command_state *cs, + bool delete, int rulenum, bool verbose) +{ + struct nft_rule_list *list; + struct nft_rule_list_iter *iter; + struct nft_rule *r; + int ret = 0; + int rule_ctr = 0; + bool found = false; + + list = nft_rule_list_get(h); + if (list == NULL) { + DEBUGP("cannot retrieve rule list from kernel\n"); + return 0; + } + + iter = nft_rule_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + r = nft_rule_list_iter_next(iter); + while (r != NULL) { + const char *rule_table = + nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE); + const char *rule_chain = + nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); + struct iptables_command_state this = {}; + + if (strcmp(table, rule_table) != 0 || + strcmp(chain, rule_chain) != 0) { + DEBUGP("different chain / table\n"); + goto next; + } + + if (rulenum >= 0) { + /* Delete by rule number case */ + if (rule_ctr != rulenum) { + rule_ctr++; + goto next; + } + } else { + /* Delete by matching rule case */ + DEBUGP("comparing with... "); +#ifdef DEBUG_DEL + nft_rule_print_save(r, 0); +#endif + + nft_rule_to_iptables_command_state(r, &this); + + if (!is_same(cs, &this)) + goto next; + + if (!find_matches(cs->matches, r)) { + DEBUGP("matches not found\n"); + goto next; + } + + if (!find_target(cs->target, r)) { + DEBUGP("target not found\n"); + goto next; + } + + if (!find_immediate(r, cs->jumpto)) { + DEBUGP("immediate not found\n"); + goto next; + } + + found = true; + break; + } +next: + r = nft_rule_list_iter_next(iter); + } + + if (found) { + ret = 1; + + if (delete) { + DEBUGP("deleting rule\n"); + __nft_rule_del(h, r); + } + } + + nft_rule_list_iter_destroy(iter); + nft_rule_list_free(list); + + if (ret == 0) + errno = ENOENT; + + return ret; +} + +int nft_rule_check(struct nft_handle *h, const char *chain, + const char *table, struct iptables_command_state *e, + bool verbose) +{ + nft_fn = nft_rule_check; + + return __nft_rule_check(h, chain, table, e, false, -1, verbose); +} + +int nft_rule_delete(struct nft_handle *h, const char *chain, + const char *table, struct iptables_command_state *e, + bool verbose) +{ + nft_fn = nft_rule_delete; + + return __nft_rule_check(h, chain, table, e, true, -1, verbose); +} + +int nft_rule_delete_num(struct nft_handle *h, const char *chain, + const char *table, int rulenum, + bool verbose) +{ + nft_fn = nft_rule_delete_num; + + return __nft_rule_check(h, chain, table, NULL, true, rulenum, verbose); +} + +int nft_rule_replace(struct nft_handle *h, const char *chain, + const char *table, struct iptables_command_state *cs, + int rulenum, bool verbose) +{ + int ret; + + nft_fn = nft_rule_replace; + + ret = __nft_rule_check(h, chain, table, NULL, true, rulenum, verbose); + if (ret < 0) + return ret; + + /* XXX needs to be inserted in position, this is appending */ + return nft_rule_add(h, chain, table, cs, true, verbose); +} + +/* + * iptables print output emulation + */ + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + +static void +print_num(uint64_t number, unsigned int format) +{ + if (format & FMT_KILOMEGAGIGA) { + if (number > 99999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + printf(FMT("%4lluT ","%lluT "), (unsigned long long)number); + } + else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number); + } + else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number); + } else + printf(FMT("%4lluK ","%lluK "), (unsigned long long)number); + } else + printf(FMT("%5llu ","%llu "), (unsigned long long)number); + } else + printf(FMT("%8llu ","%llu "), (unsigned long long)number); +} + +static void +print_header(unsigned int format, const char *chain, const char *pol, + const struct xt_counters *counters, bool basechain, uint32_t refs) +{ + printf("Chain %s", chain); + if (basechain) { + printf(" (policy %s", pol); + if (!(format & FMT_NOCOUNTS)) { + fputc(' ', stdout); + print_num(counters->pcnt, (format|FMT_NOTABLE)); + fputs("packets, ", stdout); + print_num(counters->bcnt, (format|FMT_NOTABLE)); + fputs("bytes", stdout); + } + printf(")\n"); + } else { + printf(" (%u references)\n", refs); + } + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4s ", "%s "), "num"); + if (!(format & FMT_NOCOUNTS)) { + if (format & FMT_KILOMEGAGIGA) { + printf(FMT("%5s ","%s "), "pkts"); + printf(FMT("%5s ","%s "), "bytes"); + } else { + printf(FMT("%8s ","%s "), "pkts"); + printf(FMT("%10s ","%s "), "bytes"); + } + } + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ","%s "), "target"); + fputs(" prot ", stdout); + if (format & FMT_OPTIONS) + fputs("opt", stdout); + if (format & FMT_VIA) { + printf(FMT(" %-6s ","%s "), "in"); + printf(FMT("%-6s ","%s "), "out"); + } + printf(FMT(" %-19s ","%s "), "source"); + printf(FMT(" %-19s "," %s "), "destination"); + printf("\n"); +} + +static void +print_match(struct nft_rule_expr *expr, int numeric) +{ + size_t len; + const char *match_name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); + const void *match_info = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len); + const struct xtables_match *match = + xtables_find_match(match_name, XTF_TRY_LOAD, NULL); + struct xt_entry_match *m = + calloc(1, sizeof(struct xt_entry_match) + len); + + /* emulate struct xt_entry_match since ->print needs it */ + memcpy((void *)&m->data, match_info, len); + + if (match) { + if (match->print) + /* FIXME missing first parameter */ + match->print(NULL, m, numeric); + else + printf("%s ", match_name); + } else { + if (match_name[0]) + printf("UNKNOWN match `%s' ", match_name); + } + + free(m); +} + +static void +print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, + unsigned int num, unsigned int format) +{ + const struct xtables_target *target = NULL; + const char *targname = NULL; + const void *targinfo = NULL; + uint8_t flags; + char buf[BUFSIZ]; + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + struct xt_entry_target *t; + size_t target_len = 0; + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return; + } + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "target") == 0) { + targname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME); + targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &target_len); + break; + } else if (strcmp(name, "immediate") == 0) { + uint32_t verdict = + nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); + + switch(verdict) { + case NF_ACCEPT: + targname = "ACCEPT"; + break; + case NF_DROP: + targname = "DROP"; + break; + case NFT_RETURN: + targname = "RETURN"; + break; + case NFT_GOTO: + targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN); + break; + case NFT_JUMP: + targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN); + break; + } + } + expr = nft_rule_expr_iter_next(iter); + } + nft_rule_expr_iter_destroy(iter); + + flags = cs->fw.ip.flags; + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4u ", "%u "), num); + + if (!(format & FMT_NOCOUNTS)) { + print_num(cs->counters.pcnt, format); + print_num(cs->counters.bcnt, format); + } + + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ", "%s "), targname ? targname : ""); + + fputc(cs->fw.ip.invflags & XT_INV_PROTO ? '!' : ' ', stdout); + { + const char *pname = + proto_to_name(cs->fw.ip.proto, format&FMT_NUMERIC); + if (pname) + printf(FMT("%-5s", "%s "), pname); + else + printf(FMT("%-5hu", "%hu "), cs->fw.ip.proto); + } + + if (format & FMT_OPTIONS) { + if (format & FMT_NOTABLE) + fputs("opt ", stdout); + fputc(cs->fw.ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); + fputc(' ', stdout); + } + + if (format & FMT_VIA) { + char iface[IFNAMSIZ+2]; + if (cs->fw.ip.invflags & IPT_INV_VIA_IN) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (cs->fw.ip.iniface[0] != '\0') { + strcat(iface, cs->fw.ip.iniface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT(" %-6s ","in %s "), iface); + + if (cs->fw.ip.invflags & IPT_INV_VIA_OUT) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (cs->fw.ip.outiface[0] != '\0') { + strcat(iface, cs->fw.ip.outiface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT("%-6s ","out %s "), iface); + } + + fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src)); + else + strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src)); + strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); + printf(FMT("%-19s ","%s "), buf); + } + + fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst)); + else + strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst)); + strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); + printf(FMT("%-19s ","-> %s"), buf); + } + + if (format & FMT_NOTABLE) + fputs(" ", stdout); + +#ifdef IPT_F_GOTO + if(cs->fw.ip.flags & IPT_F_GOTO) + printf("[goto] "); +#endif + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return; + } + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "match") == 0) + print_match(expr, format & FMT_NUMERIC); + + expr = nft_rule_expr_iter_next(iter); + } + nft_rule_expr_iter_destroy(iter); + + t = calloc(1, sizeof(struct xt_entry_target) + target_len); + if (t == NULL) + return; + + /* emulate struct xt_entry_match since ->print needs it */ + memcpy((void *)&t->data, targinfo, target_len); + + if (targname) { + target = xtables_find_target(targname, XTF_TRY_LOAD); + if (target) { + if (target->print) + /* FIXME missing first parameter */ + target->print(NULL, t, format & FMT_NUMERIC); + } else + printf("[%ld bytes of unknown target data] ", + target_len); + } + free(t); + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); +} + +static int +__nft_rule_list(struct nft_handle *h, struct nft_chain *c, const char *table, + int rulenum, unsigned int format, + void (*cb)(const struct iptables_command_state *cs, + struct nft_rule *r, unsigned int num, + unsigned int format)) +{ + struct nft_rule_list *list; + struct nft_rule_list_iter *iter; + struct nft_rule *r; + int rule_ctr = 0, ret = 0; + const char *chain = nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); + + list = nft_rule_list_get(h); + if (list == NULL) { + DEBUGP("cannot retrieve rule list from kernel\n"); + return 0; + } + + iter = nft_rule_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + r = nft_rule_list_iter_next(iter); + while (r != NULL) { + const char *rule_table = + nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE); + const char *rule_chain = + nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); + + rule_ctr++; + + if (strcmp(table, rule_table) != 0 || + strcmp(chain, rule_chain) != 0) + goto next; + + if (rulenum > 0) { + /* List by rule number case */ + if (rule_ctr != rulenum) { + rule_ctr++; + goto next; + } + } else { + struct iptables_command_state cs = {}; + /* Show all rules case */ + nft_rule_to_iptables_command_state(r, &cs); + + cb(&cs, r, rule_ctr, format); + } +next: + r = nft_rule_list_iter_next(iter); + } + + nft_rule_list_iter_destroy(iter); + nft_rule_list_free(list); + + if (ret == 0) + errno = ENOENT; + + return ret; +} + +int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, + int rulenum, unsigned int format) +{ + struct nft_chain_list *list; + struct nft_chain_list_iter *iter; + struct nft_chain *c; + + list = nft_chain_dump(h); + + iter = nft_chain_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + c = nft_chain_list_iter_next(iter); + while (c != NULL) { + const char *chain_table = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE); + const char *chain_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); + uint32_t policy = + nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY); + uint32_t refs = + nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_USE); + struct xt_counters ctrs = { + .pcnt = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS), + .bcnt = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES), + }; + bool basechain = false; + + if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) + basechain = true; + + if (strcmp(table, chain_table) != 0) + goto next; + if (chain && strcmp(chain, chain_name) != 0) + goto next; + + print_header(format, chain_name, policy_name[policy], &ctrs, + basechain, refs); + + /* this is a base chain */ + if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) { + __nft_rule_list(h, c, table, rulenum, format, + print_firewall); + } +next: + c = nft_chain_list_iter_next(iter); + } + + nft_chain_list_free(list); + + return 1; +} + +static void +list_save(const struct iptables_command_state *cs, struct nft_rule *r, + unsigned int num, unsigned int format) +{ + nft_rule_print_save(r, !(format & FMT_NOCOUNTS)); +} + +static int +nft_rule_list_chain_save(struct nft_handle *h, const char *table, + struct nft_chain_list *list, int counters) +{ + struct nft_chain_list_iter *iter; + struct nft_chain *c; + + iter = nft_chain_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + c = nft_chain_list_iter_next(iter); + while (c != NULL) { + const char *chain_table = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE); + const char *chain_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); + uint32_t policy = + nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY); + + if (strcmp(table, chain_table) != 0) + goto next; + + /* this is a base chain */ + if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) { + printf("-P %s %s", chain_name, policy_name[policy]); + + if (counters) { + printf(" -c %lu %lu\n", + nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS), + nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES)); + } else + printf("\n"); + } else { + printf("-N %s\n", chain_name); + } +next: + c = nft_chain_list_iter_next(iter); + } + + return 1; +} + +int nft_rule_list_save(struct nft_handle *h, const char *chain, + const char *table, int rulenum, int counters) +{ + struct nft_chain_list *list; + struct nft_chain_list_iter *iter; + struct nft_chain *c; + + list = nft_chain_dump(h); + + /* Dump policies and custom chains first */ + nft_rule_list_chain_save(h, table, list, counters); + + /* Now dump out rules in this table */ + iter = nft_chain_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + c = nft_chain_list_iter_next(iter); + while (c != NULL) { + const char *chain_table = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE); + const char *chain_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); + + if (strcmp(table, chain_table) != 0) + goto next; + if (chain && strcmp(chain, chain_name) != 0) + goto next; + + __nft_rule_list(h, c, table, rulenum, + counters ? 0 : FMT_NOCOUNTS, list_save); +next: + c = nft_chain_list_iter_next(iter); + } + + nft_chain_list_free(list); + + return 1; +} + +int nft_compatible_revision(const char *name, uint8_t rev, int opt) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, type; + int ret = 0; + + if (opt == IPT_SO_GET_REVISION_MATCH) + type = 0; + else + type = 1; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = 0; + + mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name); + mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev)); + mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type)); + + DEBUGP("requesting `%s' rev=%d type=%d via nft_compat\n", + name, rev, type); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + return 0; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + goto err; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + goto err; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + goto err; + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + goto err; + } + +err: + mnl_socket_close(nl); + + return ret < 0 ? 0 : 1; +} + +/* Translates errno numbers into more human-readable form than strerror. */ +const char *nft_strerror(int err) +{ + unsigned int i; + static struct table_struct { + void *fn; + int err; + const char *message; + } table[] = + { + { nft_chain_user_del, ENOTEMPTY, "Chain is not empty" }, + { nft_chain_user_del, EINVAL, "Can't delete built-in chain" }, + { nft_chain_user_del, EMLINK, + "Can't delete chain with references left" }, + { nft_chain_user_add, EEXIST, "Chain already exists" }, + { nft_rule_add, E2BIG, "Index of insertion too big" }, + { nft_rule_replace, E2BIG, "Index of replacement too big" }, + { nft_rule_delete_num, E2BIG, "Index of deletion too big" }, +/* { TC_READ_COUNTER, E2BIG, "Index of counter too big" }, + { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" }, */ + { nft_rule_add, ELOOP, "Loop found in table" }, + { nft_rule_add, EINVAL, "Target problem" }, + /* ENOENT for DELETE probably means no matching rule */ + { nft_rule_delete, ENOENT, + "Bad rule (does a matching rule exist in that chain?)" }, + { nft_chain_set, ENOENT, "Bad built-in chain name" }, + { nft_chain_set, EINVAL, "Bad policy name" }, + { NULL, EPERM, "Permission denied (you must be root)" }, + { NULL, 0, "Incompatible with this kernel" }, + { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" }, + { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" }, + { NULL, ENOMEM, "Memory allocation problem" }, + { NULL, ENOENT, "No chain/target/match by that name" }, + }; + + for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) { + if ((!table[i].fn || table[i].fn == nft_fn) + && table[i].err == err) + return table[i].message; + } + + return strerror(err); +} diff --git a/iptables/nft.h b/iptables/nft.h new file mode 100644 index 00000000..f5a9efb8 --- /dev/null +++ b/iptables/nft.h @@ -0,0 +1,62 @@ +#ifndef _NFT_H_ +#define _NFT_H_ + +#include "xshared.h" + +struct nft_handle { + struct mnl_socket *nl; + uint32_t portid; + uint32_t seq; +}; + +int nft_init(struct nft_handle *h); +void nft_fini(struct nft_handle *h); + +/* + * Operations with tables. + */ +struct nft_table; + +int nft_table_add(struct nft_handle *h, const struct nft_table *t); +int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, bool counters), bool counters); +bool nft_table_find(struct nft_handle *h, const char *tablename); + +/* + * Operations with chains. + */ +struct nft_chain; + +int nft_chain_add(struct nft_handle *h, const struct nft_chain *c); +int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters); +struct nft_chain_list *nft_chain_dump(struct nft_handle *h); +int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, const char *table); +int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table); +int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table); +int nft_chain_user_rename(struct nft_handle *h, const char *chain, const char *table, const char *newname); + +/* + * Operations with rule-set. + */ +struct nft_rule; + +int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool append, bool verbose); +int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); +int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); +int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose); +int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, int rulenum, bool verbose); +int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format); +int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *table, int rulenum, int counters); +int nft_rule_save(struct nft_handle *h, const char *table, bool counters); +int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table); + +/* + * revision compatibility. + */ +int nft_compatible_revision(const char *name, uint8_t rev, int opt); + +/* + * Error reporting. + */ +const char *nft_strerror(int err); + +#endif diff --git a/iptables/xshared.h b/iptables/xshared.h index 1e2b9b8e..27c5b786 100644 --- a/iptables/xshared.h +++ b/iptables/xshared.h @@ -58,6 +58,7 @@ struct iptables_command_state { unsigned int options; struct xtables_rule_match *matches; struct xtables_target *target; + struct xt_counters counters; char *protocol; int proto_used; const char *jumpto; diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y new file mode 100644 index 00000000..fe5bcbf1 --- /dev/null +++ b/iptables/xtables-config-parser.y @@ -0,0 +1,213 @@ +%{ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software has been sponsored by Sophos Astaro + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern char *yytext; +extern int yylineno; + +static LIST_HEAD(xtables_stack); + +struct stack_elem { + struct list_head head; + int token; + size_t size; + char data[]; +}; + +static void *stack_push(int token, size_t size) +{ + struct stack_elem *e; + + e = calloc(1, sizeof(struct stack_elem) + size); + + e->token = token; + e->size = size; + + list_add(&e->head, &xtables_stack); + + return e->data; +} + +static struct stack_elem *stack_pop(void) +{ + struct stack_elem *e; + + e = list_entry(xtables_stack.next, struct stack_elem, head); + + if (&e->head == &xtables_stack) + return NULL; + + list_del(&e->head); + return e; +} + +static inline void stack_put_i32(void *data, int value) +{ + memcpy(data, &value, sizeof(int)); +} + +static inline void stack_put_str(void *data, const char *str) +{ + memcpy(data, str, strlen(str)+1); +} + +static void stack_free(struct stack_elem *e) +{ + free(e); +} + +%} + +%union { + int val; + char *string; +} + +%token T_TABLE +%token T_CHAIN +%token T_HOOK +%token T_PRIO + +%token T_STRING +%token T_INTEGER + +%% + +configfile : + | lines + ; + +lines : line + | lines line + ; + +line : table + ; + +table : T_TABLE T_STRING T_PRIO T_INTEGER '{' chains '}' + { + /* added in reverse order to pop it in order */ + void *data = stack_push(T_PRIO, sizeof(int32_t)); + stack_put_i32(data, $4); + data = stack_push(T_TABLE, strlen($2)); + stack_put_str(data, $2); + } + ; + +chains : chain + | chains chain + ; + +chain : T_CHAIN T_STRING T_HOOK T_STRING + { + /* added in reverse order to pop it in order */ + void *data = stack_push(T_HOOK, strlen($4)); + stack_put_str(data, $4); + data = stack_push(T_CHAIN, strlen($2)); + stack_put_str(data, $2); + } + ; + +%% + +int __attribute__((noreturn)) +yyerror(char *msg) +{ + fprintf(stderr, "parsing config file in line (%d), symbol '%s': %s\n", + yylineno, yytext, msg); + exit(EXIT_FAILURE); +} + +static int hooknametonum(const char *hookname) +{ + if (strcmp(hookname, "NF_INET_LOCAL_IN") == 0) + return NF_INET_LOCAL_IN; + else if (strcmp(hookname, "NF_INET_FORWARD") == 0) + return NF_INET_FORWARD; + else if (strcmp(hookname, "NF_INET_LOCAL_OUT") == 0) + return NF_INET_LOCAL_OUT; + else if (strcmp(hookname, "NF_INET_PRE_ROUTING") == 0) + return NF_INET_PRE_ROUTING; + else if (strcmp(hookname, "NF_INET_POST_ROUTING") == 0) + return NF_INET_POST_ROUTING; + + return -1; +} + +int xtables_config_parse(char *filename, struct nft_table_list *table_list, + struct nft_chain_list *chain_list) +{ + FILE *fp; + struct stack_elem *e; + struct nft_table *table = NULL; + struct nft_chain *chain = NULL; + int prio = 0; + + fp = fopen(filename, "r"); + if (!fp) + return -1; + + yyrestart(fp); + yyparse(); + fclose(fp); + + for (e = stack_pop(); e != NULL; e = stack_pop()) { + switch(e->token) { + case T_TABLE: + table = nft_table_alloc(); + if (table == NULL) { + perror("nft_table_alloc"); + return -1; + } + nft_table_attr_set(table, NFT_TABLE_ATTR_NAME, e->data); + nft_table_list_add(table, table_list); + break; + case T_PRIO: + prio = *((int32_t *)e->data); + break; + case T_CHAIN: + chain = nft_chain_alloc(); + if (chain == NULL) { + perror("nft_chain_alloc"); + return -1; + } + nft_chain_attr_set(chain, NFT_CHAIN_ATTR_TABLE, + (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); + nft_chain_attr_set(chain, NFT_CHAIN_ATTR_NAME, e->data); + nft_chain_list_add(chain, chain_list); + break; + case T_HOOK: + nft_chain_attr_set_u32(chain, NFT_CHAIN_ATTR_HOOKNUM, + hooknametonum(e->data)); + nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio); + break; + default: + printf("unknown token type %d\n", e->token); + break; + } + stack_free(e); + } + + return 0; +} diff --git a/iptables/xtables-config-syntax.l b/iptables/xtables-config-syntax.l new file mode 100644 index 00000000..7a66ef39 --- /dev/null +++ b/iptables/xtables-config-syntax.l @@ -0,0 +1,53 @@ +%{ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software has been sponsored by Sophos Astaro + */ + +#include +#include "xtables-config-parser.h" +%} + +%option yylineno +%option noinput +%option nounput + +ws [ \t]+ +comment #.*$ +nl [\n\r] + +is_on [o|O][n|N] +is_off [o|O][f|F][f|F] +integer [\-\+]?[0-9]+ +string [a-zA-Z][a-zA-Z0-9\.\-\_]* + +%% +"table" { return T_TABLE; } +"chain" { return T_CHAIN; } +"hook" { return T_HOOK; } +"prio" { return T_PRIO; } + +{integer} { yylval.val = atoi(yytext); return T_INTEGER; } +{string} { yylval.string = strdup(yytext); return T_STRING; } + +{comment} ; +{ws} ; +{nl} ; + +<> { yyterminate(); } + +. { return yytext[0]; } + +%% + +int +yywrap() +{ + return 1; +} diff --git a/iptables/xtables-config.c b/iptables/xtables-config.c new file mode 100644 index 00000000..16918bf6 --- /dev/null +++ b/iptables/xtables-config.c @@ -0,0 +1,107 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xtables-multi.h" +#include "xtables-config-parser.h" + +#include "nft.h" + +extern int xtables_config_parse(const char *filename, + struct nft_table_list *table_list, + struct nft_chain_list *chain_list); + +#define XTABLES_CONFIG_DEFAULT "/etc/xtables.conf" + +int xtables_config_main(int argc, char *argv[]) +{ + struct nft_table_list *table_list = nft_table_list_alloc(); + struct nft_chain_list *chain_list = nft_chain_list_alloc(); + struct nft_table_list_iter *titer; + struct nft_chain_list_iter *citer; + struct nft_table *table; + struct nft_chain *chain; + const char *filename = NULL; + struct nft_handle h; + + if (argc > 2) { + fprintf(stderr, "Usage: %s []\n", argv[0]); + return EXIT_SUCCESS; + } + if (argc == 1) + filename = XTABLES_CONFIG_DEFAULT; + else + filename = argv[1]; + + if (xtables_config_parse(filename, table_list, chain_list) < 0) { + if (errno == ENOENT) { + fprintf(stderr, "configuration file `%s' does not " + "exists\n", filename); + } else { + fprintf(stderr, "Fatal error: %s\n", strerror(errno)); + } + return EXIT_FAILURE; + } + + nft_init(&h); + + /* Stage 1) create tables */ + titer = nft_table_list_iter_create(table_list); + while ((table = nft_table_list_iter_next(titer)) != NULL) { + if (nft_table_add(&h, table) < 0) { + if (errno == EEXIST) { + printf("table `%s' already exists, skipping\n", + (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); + } else { + printf("table `%s' cannot be create, reason `%s'. Exitting\n", + (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME), + strerror(errno)); + return EXIT_FAILURE; + } + continue; + } + printf("table `%s' has been created\n", + (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); + } + + /* Stage 2) create chains */ + citer = nft_chain_list_iter_create(chain_list); + while ((chain = nft_chain_list_iter_next(citer)) != NULL) { + if (nft_chain_add(&h, chain) < 0) { + if (errno == EEXIST) { + printf("chain `%s' already exists in table `%s', skipping\n", + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE)); + } else { + printf("chain `%s' cannot be create, reason `%s'. Exitting\n", + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), + strerror(errno)); + return EXIT_FAILURE; + } + continue; + } + + printf("chain `%s' in table `%s' has been created\n", + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE)); + } + + return EXIT_SUCCESS; +} diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c index 8014d5fb..c1746434 100644 --- a/iptables/xtables-multi.c +++ b/iptables/xtables-multi.c @@ -13,6 +13,10 @@ #include "ip6tables-multi.h" #endif +#ifdef ENABLE_NFTABLES +#include "xtables-multi.h" +#endif + static const struct subcommand multi_subcommands[] = { #ifdef ENABLE_IPV4 {"iptables", iptables_main}, @@ -31,6 +35,12 @@ static const struct subcommand multi_subcommands[] = { {"save6", ip6tables_save_main}, {"ip6tables-restore", ip6tables_restore_main}, {"restore6", ip6tables_restore_main}, +#endif +#ifdef ENABLE_NFTABLES + {"xtables", xtables_main}, + {"xtables-save", xtables_save_main}, + {"xtables-restore", xtables_restore_main}, + {"xtables-config", xtables_config_main}, #endif {NULL}, }; diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h index 615724b1..be2b3ad8 100644 --- a/iptables/xtables-multi.h +++ b/iptables/xtables-multi.h @@ -2,5 +2,9 @@ #define _XTABLES_MULTI_H 1 extern int iptables_xml_main(int, char **); +extern int xtables_main(int, char **); +extern int xtables_save_main(int, char **); +extern int xtables_restore_main(int, char **); +extern int xtables_config_main(int, char **); #endif /* _XTABLES_MULTI_H */ diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c new file mode 100644 index 00000000..09922a0c --- /dev/null +++ b/iptables/xtables-restore.c @@ -0,0 +1,417 @@ +/* Code to restore the iptables state, from file by iptables-save. + * (C) 2000-2002 by Harald Welte + * based on previous code from Rusty Russell + * + * This code is distributed under the terms of GNU GPL v2 + */ + +#include +#include +#include +#include +#include +#include +#include "iptables.h" +#include "xtables.h" +#include "libiptc/libiptc.h" +#include "xtables-multi.h" +#include "nft.h" + +#ifdef DEBUG +#define DEBUGP(x, args...) fprintf(stderr, x, ## args) +#else +#define DEBUGP(x, args...) +#endif + +static int binary = 0, counters = 0, verbose = 0, noflush = 0; + +/* Keeping track of external matches and targets. */ +static const struct option options[] = { + {.name = "binary", .has_arg = false, .val = 'b'}, + {.name = "counters", .has_arg = false, .val = 'c'}, + {.name = "verbose", .has_arg = false, .val = 'v'}, + {.name = "test", .has_arg = false, .val = 't'}, + {.name = "help", .has_arg = false, .val = 'h'}, + {.name = "noflush", .has_arg = false, .val = 'n'}, + {.name = "modprobe", .has_arg = true, .val = 'M'}, + {.name = "table", .has_arg = true, .val = 'T'}, + {NULL}, +}; + +static void print_usage(const char *name, const char *version) __attribute__((noreturn)); + +#define prog_name xtables_globals.program_name + +static void print_usage(const char *name, const char *version) +{ + fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n" + " [ --binary ]\n" + " [ --counters ]\n" + " [ --verbose ]\n" + " [ --test ]\n" + " [ --help ]\n" + " [ --noflush ]\n" + " [ --table= ]\n" + " [ --modprobe=]\n", name); + + exit(1); +} + +static int parse_counters(char *string, struct xt_counters *ctr) +{ + unsigned long long pcnt, bcnt; + int ret; + + ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt); + ctr->pcnt = pcnt; + ctr->bcnt = bcnt; + return ret == 2; +} + +/* global new argv and argc */ +static char *newargv[255]; +static int newargc; + +/* function adding one argument to newargv, updating newargc + * returns true if argument added, false otherwise */ +static int add_argv(char *what) { + DEBUGP("add_argv: %s\n", what); + if (what && newargc + 1 < ARRAY_SIZE(newargv)) { + newargv[newargc] = strdup(what); + newargv[++newargc] = NULL; + return 1; + } else { + xtables_error(PARAMETER_PROBLEM, + "Parser cannot handle more arguments\n"); + return 0; + } +} + +static void free_argv(void) { + int i; + + for (i = 0; i < newargc; i++) + free(newargv[i]); +} + +static void add_param_to_argv(char *parsestart) +{ + int quote_open = 0, escaped = 0, param_len = 0; + char param_buffer[1024], *curchar; + + /* After fighting with strtok enough, here's now + * a 'real' parser. According to Rusty I'm now no + * longer a real hacker, but I can live with that */ + + for (curchar = parsestart; *curchar; curchar++) { + if (quote_open) { + if (escaped) { + param_buffer[param_len++] = *curchar; + escaped = 0; + continue; + } else if (*curchar == '\\') { + escaped = 1; + continue; + } else if (*curchar == '"') { + quote_open = 0; + *curchar = ' '; + } else { + param_buffer[param_len++] = *curchar; + continue; + } + } else { + if (*curchar == '"') { + quote_open = 1; + continue; + } + } + + if (*curchar == ' ' + || *curchar == '\t' + || * curchar == '\n') { + if (!param_len) { + /* two spaces? */ + continue; + } + + param_buffer[param_len] = '\0'; + + /* check if table name specified */ + if (!strncmp(param_buffer, "-t", 2) + || !strncmp(param_buffer, "--table", 8)) { + xtables_error(PARAMETER_PROBLEM, + "The -t option (seen in line %u) cannot be " + "used in xtables-restore.\n", line); + exit(1); + } + + add_argv(param_buffer); + param_len = 0; + } else { + /* regular character, copy to buffer */ + param_buffer[param_len++] = *curchar; + + if (param_len >= sizeof(param_buffer)) + xtables_error(PARAMETER_PROBLEM, + "Parameter too long!"); + } + } +} + +int +xtables_restore_main(int argc, char *argv[]) +{ + struct nft_handle h; + char buffer[10240]; + int c; + char curtable[XT_TABLE_MAXNAMELEN + 1]; + FILE *in; + int in_table = 0, testing = 0; + const char *tablename = NULL; + const struct xtc_ops *ops = &iptc_ops; + + line = 0; + + xtables_globals.program_name = "xtables-restore"; + c = xtables_init_all(&xtables_globals, NFPROTO_IPV4); + if (c < 0) { + fprintf(stderr, "%s/%s Failed to initialize xtables\n", + xtables_globals.program_name, + xtables_globals.program_version); + exit(1); + } +#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) + init_extensions(); + init_extensions4(); +#endif + + nft_init(&h); + + while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) { + switch (c) { + case 'b': + binary = 1; + break; + case 'c': + counters = 1; + break; + case 'v': + verbose = 1; + break; + case 't': + testing = 1; + break; + case 'h': + print_usage("xtables-restore", + IPTABLES_VERSION); + break; + case 'n': + noflush = 1; + break; + case 'M': + xtables_modprobe_program = optarg; + break; + case 'T': + tablename = optarg; + break; + } + } + + if (optind == argc - 1) { + in = fopen(argv[optind], "re"); + if (!in) { + fprintf(stderr, "Can't open %s: %s\n", argv[optind], + strerror(errno)); + exit(1); + } + } + else if (optind < argc) { + fprintf(stderr, "Unknown arguments found on commandline\n"); + exit(1); + } + else in = stdin; + + /* Grab standard input. */ + while (fgets(buffer, sizeof(buffer), in)) { + int ret = 0; + + line++; + if (buffer[0] == '\n') + continue; + else if (buffer[0] == '#') { + if (verbose) + fputs(buffer, stdout); + continue; + } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) { + /* FIXME commit/testing operation not supported */ + if (!testing) { + DEBUGP("Calling commit\n"); + ret = 1; + } else { + DEBUGP("Not calling commit, testing\n"); + ret = 1; + } + in_table = 0; + } else if ((buffer[0] == '*') && (!in_table)) { + /* New table */ + char *table; + + table = strtok(buffer+1, " \t\n"); + DEBUGP("line %u, table '%s'\n", line, table); + if (!table) { + xtables_error(PARAMETER_PROBLEM, + "%s: line %u table name invalid\n", + xt_params->program_name, line); + exit(1); + } + strncpy(curtable, table, XT_TABLE_MAXNAMELEN); + curtable[XT_TABLE_MAXNAMELEN] = '\0'; + + if (tablename && (strcmp(tablename, table) != 0)) + continue; + + if (noflush == 0) { + DEBUGP("Cleaning all chains of table '%s'\n", + table); + nft_rule_flush(&h, NULL, table); + + DEBUGP("Deleting all user-defined chains " + "of table '%s'\n", table); + nft_chain_user_del(&h, NULL, table); + } + + ret = 1; + in_table = 1; + + } else if ((buffer[0] == ':') && (in_table)) { + /* New chain. */ + char *policy, *chain = NULL; + struct xt_counters count = {}; + + chain = strtok(buffer+1, " \t\n"); + DEBUGP("line %u, chain '%s'\n", line, chain); + if (!chain) { + xtables_error(PARAMETER_PROBLEM, + "%s: line %u chain name invalid\n", + xt_params->program_name, line); + exit(1); + } + + if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN) + xtables_error(PARAMETER_PROBLEM, + "Invalid chain name `%s' " + "(%u chars max)", + chain, XT_EXTENSION_MAXNAMELEN - 1); + + policy = strtok(NULL, " \t\n"); + DEBUGP("line %u, policy '%s'\n", line, policy); + if (!policy) { + xtables_error(PARAMETER_PROBLEM, + "%s: line %u policy invalid\n", + xt_params->program_name, line); + exit(1); + } + + if (strcmp(policy, "-") != 0) { + if (counters) { + char *ctrs; + ctrs = strtok(NULL, " \t\n"); + + if (!ctrs || !parse_counters(ctrs, &count)) + xtables_error(PARAMETER_PROBLEM, + "invalid policy counters " + "for chain '%s'\n", chain); + + } + + DEBUGP("Setting policy of chain %s to %s\n", + chain, policy); + } + + if (nft_chain_set(&h, curtable, chain, policy, &count) < 0) { + xtables_error(OTHER_PROBLEM, + "Can't set policy `%s'" + " on `%s' line %u: %s\n", + policy, chain, line, + ops->strerror(errno)); + } + + ret = 1; + + } else if (in_table) { + int a; + char *ptr = buffer; + char *pcnt = NULL; + char *bcnt = NULL; + char *parsestart; + + /* reset the newargv */ + newargc = 0; + + if (buffer[0] == '[') { + /* we have counters in our input */ + ptr = strchr(buffer, ']'); + if (!ptr) + xtables_error(PARAMETER_PROBLEM, + "Bad line %u: need ]\n", + line); + + pcnt = strtok(buffer+1, ":"); + if (!pcnt) + xtables_error(PARAMETER_PROBLEM, + "Bad line %u: need :\n", + line); + + bcnt = strtok(NULL, "]"); + if (!bcnt) + xtables_error(PARAMETER_PROBLEM, + "Bad line %u: need ]\n", + line); + + /* start command parsing after counter */ + parsestart = ptr + 1; + } else { + /* start command parsing at start of line */ + parsestart = buffer; + } + + add_argv(argv[0]); + add_argv("-t"); + add_argv(curtable); + + if (counters && pcnt && bcnt) { + add_argv("--set-counters"); + add_argv((char *) pcnt); + add_argv((char *) bcnt); + } + + add_param_to_argv(parsestart); + + DEBUGP("calling do_command4(%u, argv, &%s, handle):\n", + newargc, curtable); + + for (a = 0; a < newargc; a++) + DEBUGP("argv[%u]: %s\n", a, newargv[a]); + + ret = do_commandx(&h, newargc, newargv, &newargv[2]); + + free_argv(); + fflush(stdout); + } + if (tablename && (strcmp(tablename, curtable) != 0)) + continue; + if (!ret) { + fprintf(stderr, "%s: line %u failed\n", + xt_params->program_name, line); + exit(1); + } + } + if (in_table) { + fprintf(stderr, "%s: COMMIT expected at line %u\n", + xt_params->program_name, line + 1); + exit(1); + } + + fclose(in); + return 0; +} diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c new file mode 100644 index 00000000..046c948d --- /dev/null +++ b/iptables/xtables-save.c @@ -0,0 +1,122 @@ +/* Code to save the xtables state, in human readable-form. */ +/* (C) 1999 by Paul 'Rusty' Russell and + * (C) 2000-2002 by Harald Welte + * (C) 2012 by Pablo Neira Ayuso + * + * This code is distributed under the terms of GNU GPL v2 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "libiptc/libiptc.h" +#include "iptables.h" +#include "xtables-multi.h" +#include "nft.h" + +#include + +#ifndef NO_SHARED_LIBS +#include +#endif + +static bool show_counters = false; + +static const struct option options[] = { + {.name = "counters", .has_arg = false, .val = 'c'}, + {.name = "dump", .has_arg = false, .val = 'd'}, + {.name = "table", .has_arg = true, .val = 't'}, + {.name = "modprobe", .has_arg = true, .val = 'M'}, + {NULL}, +}; + +static int +do_output(struct nft_handle *h, const char *tablename, bool counters) +{ + struct nft_chain_list *chain_list; + + if (!tablename) + return nft_for_each_table(h, do_output, counters); + + if (!nft_table_find(h, tablename)) { + printf("Table `%s' does not exist\n", tablename); + return 0; + } + + chain_list = nft_chain_dump(h); + + time_t now = time(NULL); + + printf("# Generated by xtables-save v%s on %s", + IPTABLES_VERSION, ctime(&now)); + printf("*%s\n", tablename); + + /* Dump out chain names first, + * thereby preventing dependency conflicts */ + nft_chain_save(h, chain_list, tablename); + nft_rule_save(h, tablename, counters); + + now = time(NULL); + printf("COMMIT\n"); + printf("# Completed on %s", ctime(&now)); + + return 1; +} + +/* Format: + * :Chain name POLICY packets bytes + * rule + */ +int +xtables_save_main(int argc, char *argv[]) +{ + const char *tablename = NULL; + struct nft_handle h; + int c; + + xtables_globals.program_name = "xtables-save"; + /* XXX xtables_init_all does several things we don't want */ + c = xtables_init_all(&xtables_globals, NFPROTO_IPV4); + if (c < 0) { + fprintf(stderr, "%s/%s Failed to initialize xtables\n", + xtables_globals.program_name, + xtables_globals.program_version); + exit(1); + } +#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) + init_extensions(); + init_extensions4(); +#endif + nft_init(&h); + + while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) { + switch (c) { + case 'c': + show_counters = true; + break; + + case 't': + /* Select specific table. */ + tablename = optarg; + break; + case 'M': + xtables_modprobe_program = optarg; + break; + case 'd': + do_output(&h, tablename, show_counters); + exit(0); + } + } + + if (optind < argc) { + fprintf(stderr, "Unknown arguments found on commandline\n"); + exit(1); + } + + return !do_output(&h, tablename, show_counters); +} diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c new file mode 100644 index 00000000..f746c902 --- /dev/null +++ b/iptables/xtables-standalone.c @@ -0,0 +1,80 @@ +/* + * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au + * + * Based on the ipchains code by Paul Russell and Michael Neuling + * + * (C) 2000-2002 by the netfilter coreteam : + * Paul 'Rusty' Russell + * Marc Boucher + * James Morris + * Harald Welte + * Jozsef Kadlecsik + * + * iptables -- IP firewall administration for kernels with + * firewall table (aimed for the 2.3 kernels) + * + * See the accompanying manual page iptables(8) for information + * about proper usage of this program. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include "xtables-multi.h" +#include "nft.h" + +int +xtables_main(int argc, char *argv[]) +{ + int ret; + char *table = "filter"; + struct nft_handle h; + + iptables_globals.program_name = "xtables"; + ret = xtables_init_all(&xtables_globals, NFPROTO_IPV4); + if (ret < 0) { + fprintf(stderr, "%s/%s Failed to initialize xtables\n", + iptables_globals.program_name, + iptables_globals.program_version); + exit(1); + } +#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) + init_extensions(); + init_extensions4(); +#endif + + nft_init(&h); + + ret = do_commandx(&h, argc, argv, &table); + if (!ret) { + if (errno == EINVAL) { + fprintf(stderr, "iptables: %s. " + "Run `dmesg' for more information.\n", + nft_strerror(errno)); + } else { + fprintf(stderr, "iptables: %s.\n", + nft_strerror(errno)); + } + if (errno == EAGAIN) { + exit(RESOURCE_PROBLEM); + } + } + + exit(!ret); +} diff --git a/iptables/xtables.c b/iptables/xtables.c new file mode 100644 index 00000000..a6875757 --- /dev/null +++ b/iptables/xtables.c @@ -0,0 +1,1251 @@ +/* Code to take an iptables-style command line and do it. */ + +/* + * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au + * + * (C) 2000-2002 by the netfilter coreteam : + * Paul 'Rusty' Russell + * Marc Boucher + * James Morris + * Harald Welte + * Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xshared.h" +#include "nft.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + + +#define CMD_NONE 0x0000U +#define CMD_INSERT 0x0001U +#define CMD_DELETE 0x0002U +#define CMD_DELETE_NUM 0x0004U +#define CMD_REPLACE 0x0008U +#define CMD_APPEND 0x0010U +#define CMD_LIST 0x0020U +#define CMD_FLUSH 0x0040U +#define CMD_ZERO 0x0080U +#define CMD_NEW_CHAIN 0x0100U +#define CMD_DELETE_CHAIN 0x0200U +#define CMD_SET_POLICY 0x0400U +#define CMD_RENAME_CHAIN 0x0800U +#define CMD_LIST_RULES 0x1000U +#define CMD_ZERO_NUM 0x2000U +#define CMD_CHECK 0x4000U +#define NUMBER_OF_CMD 16 +static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', + 'N', 'X', 'P', 'E', 'S', 'Z', 'C' }; + +#define OPT_FRAGMENT 0x00800U +#define NUMBER_OF_OPT ARRAY_SIZE(optflags) +static const char optflags[] += { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f'}; + +static struct option original_opts[] = { + {.name = "append", .has_arg = 1, .val = 'A'}, + {.name = "delete", .has_arg = 1, .val = 'D'}, + {.name = "check", .has_arg = 1, .val = 'C'}, + {.name = "insert", .has_arg = 1, .val = 'I'}, + {.name = "replace", .has_arg = 1, .val = 'R'}, + {.name = "list", .has_arg = 2, .val = 'L'}, + {.name = "list-rules", .has_arg = 2, .val = 'S'}, + {.name = "flush", .has_arg = 2, .val = 'F'}, + {.name = "zero", .has_arg = 2, .val = 'Z'}, + {.name = "new-chain", .has_arg = 1, .val = 'N'}, + {.name = "delete-chain", .has_arg = 2, .val = 'X'}, + {.name = "rename-chain", .has_arg = 1, .val = 'E'}, + {.name = "policy", .has_arg = 1, .val = 'P'}, + {.name = "source", .has_arg = 1, .val = 's'}, + {.name = "destination", .has_arg = 1, .val = 'd'}, + {.name = "src", .has_arg = 1, .val = 's'}, /* synonym */ + {.name = "dst", .has_arg = 1, .val = 'd'}, /* synonym */ + {.name = "protocol", .has_arg = 1, .val = 'p'}, + {.name = "in-interface", .has_arg = 1, .val = 'i'}, + {.name = "jump", .has_arg = 1, .val = 'j'}, + {.name = "table", .has_arg = 1, .val = 't'}, + {.name = "match", .has_arg = 1, .val = 'm'}, + {.name = "numeric", .has_arg = 0, .val = 'n'}, + {.name = "out-interface", .has_arg = 1, .val = 'o'}, + {.name = "verbose", .has_arg = 0, .val = 'v'}, + {.name = "exact", .has_arg = 0, .val = 'x'}, + {.name = "fragments", .has_arg = 0, .val = 'f'}, + {.name = "version", .has_arg = 0, .val = 'V'}, + {.name = "help", .has_arg = 2, .val = 'h'}, + {.name = "line-numbers", .has_arg = 0, .val = '0'}, + {.name = "modprobe", .has_arg = 1, .val = 'M'}, + {.name = "set-counters", .has_arg = 1, .val = 'c'}, + {.name = "goto", .has_arg = 1, .val = 'g'}, + {.name = "ipv4", .has_arg = 0, .val = '4'}, + {.name = "ipv6", .has_arg = 0, .val = '6'}, + {NULL}, +}; + +void iptables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); + +struct xtables_globals xtables_globals = { + .option_offset = 0, + .program_version = IPTABLES_VERSION, + .orig_opts = original_opts, + .exit_err = iptables_exit_error, + .compat_rev = nft_compatible_revision, +}; + +/* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal (applies to + * CMD_LIST and CMD_ZERO only). + * Key: + * + compulsory + * x illegal + * optional + */ + +static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = +/* Well, it's better than "Re: Linux vs FreeBSD" */ +{ + /* -n -s -d -p -j -v -x -i -o --line -c -f */ +/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '}, +/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '}, +/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '}, +/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '}, +/*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' ','x','x'}, +/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x'}, +/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*CHECK*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '}, +}; + +static const int inverse_for_options[NUMBER_OF_OPT] = +{ +/* -n */ 0, +/* -s */ IPT_INV_SRCIP, +/* -d */ IPT_INV_DSTIP, +/* -p */ XT_INV_PROTO, +/* -j */ 0, +/* -v */ 0, +/* -x */ 0, +/* -i */ IPT_INV_VIA_IN, +/* -o */ IPT_INV_VIA_OUT, +/*--line*/ 0, +/* -c */ 0, +/* -f */ IPT_INV_FRAG, +}; + +#define opts xtables_globals.opts +#define prog_name xtables_globals.program_name +#define prog_vers xtables_globals.program_version + +/* Primitive headers... */ +/* defined in netinet/in.h */ +#if 0 +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif +#endif + +enum { + IPT_DOTTED_ADDR = 0, + IPT_DOTTED_MASK +}; + +static void __attribute__((noreturn)) +exit_tryhelp(int status) +{ + if (line != -1) + fprintf(stderr, "Error occurred at line: %d\n", line); + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + prog_name, prog_name); + xtables_free_opts(1); + exit(status); +} + +static void +exit_printhelp(const struct xtables_rule_match *matches) +{ + printf("%s v%s\n\n" +"Usage: %s -[ACD] chain rule-specification [options]\n" +" %s -I chain [rulenum] rule-specification [options]\n" +" %s -R chain rulenum rule-specification [options]\n" +" %s -D chain rulenum [options]\n" +" %s -[LS] [chain [rulenum]] [options]\n" +" %s -[FZ] [chain] [options]\n" +" %s -[NX] chain\n" +" %s -E old-chain-name new-chain-name\n" +" %s -P chain target [options]\n" +" %s -h (print this help information)\n\n", + prog_name, prog_vers, prog_name, prog_name, + prog_name, prog_name, prog_name, prog_name, + prog_name, prog_name, prog_name, prog_name); + + printf( +"Commands:\n" +"Either long or short options are allowed.\n" +" --append -A chain Append to chain\n" +" --check -C chain Check for the existence of a rule\n" +" --delete -D chain Delete matching rule from chain\n" +" --delete -D chain rulenum\n" +" Delete rule rulenum (1 = first) from chain\n" +" --insert -I chain [rulenum]\n" +" Insert in chain as rulenum (default 1=first)\n" +" --replace -R chain rulenum\n" +" Replace rule rulenum (1 = first) in chain\n" +" --list -L [chain [rulenum]]\n" +" List the rules in a chain or all chains\n" +" --list-rules -S [chain [rulenum]]\n" +" Print the rules in a chain or all chains\n" +" --flush -F [chain] Delete all rules in chain or all chains\n" +" --zero -Z [chain [rulenum]]\n" +" Zero counters in chain or all chains\n" +" --new -N chain Create a new user-defined chain\n" +" --delete-chain\n" +" -X [chain] Delete a user-defined chain\n" +" --policy -P chain target\n" +" Change policy on chain to target\n" +" --rename-chain\n" +" -E old-chain new-chain\n" +" Change chain name, (moving any references)\n" + +"Options:\n" +" --ipv4 -4 Nothing (line is ignored by ip6tables-restore)\n" +" --ipv6 -6 Error (line is ignored by iptables-restore)\n" +"[!] --proto -p proto protocol: by number or name, eg. `tcp'\n" +"[!] --source -s address[/mask][...]\n" +" source specification\n" +"[!] --destination -d address[/mask][...]\n" +" destination specification\n" +"[!] --in-interface -i input name[+]\n" +" network interface name ([+] for wildcard)\n" +" --jump -j target\n" +" target for rule (may load target extension)\n" +#ifdef IPT_F_GOTO +" --goto -g chain\n" +" jump to chain with no return\n" +#endif +" --match -m match\n" +" extended match (may load extension)\n" +" --numeric -n numeric output of addresses and ports\n" +"[!] --out-interface -o output name[+]\n" +" network interface name ([+] for wildcard)\n" +" --table -t table table to manipulate (default: `filter')\n" +" --verbose -v verbose mode\n" +" --line-numbers print line numbers when listing\n" +" --exact -x expand numbers (display exact values)\n" +"[!] --fragment -f match second or further fragments only\n" +" --modprobe= try to insert modules using this command\n" +" --set-counters PKTS BYTES set the counter during insert/append\n" +"[!] --version -V print package version.\n"); + + print_extension_helps(xtables_targets, matches); + exit(0); +} + +static void +generic_opt_check(int command, int options) +{ + int i, j, legal = 0; + + /* Check that commands are valid with options. Complicated by the + * fact that if an option is legal with *any* command given, it is + * legal overall (ie. -z and -l). + */ + for (i = 0; i < NUMBER_OF_OPT; i++) { + legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ + + for (j = 0; j < NUMBER_OF_CMD; j++) { + if (!(command & (1< 1; option >>= 1, ptr++); + + return *ptr; +} + +static char +cmd2char(int option) +{ + const char *ptr; + for (ptr = cmdflags; option > 1; option >>= 1, ptr++); + + return *ptr; +} + +static void +add_command(unsigned int *cmd, const int newcmd, const int othercmds, + int invert) +{ + if (invert) + xtables_error(PARAMETER_PROBLEM, "unexpected ! flag"); + if (*cmd & (~othercmds)) + xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n", + cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); + *cmd |= newcmd; +} + +/* + * All functions starting with "parse" should succeed, otherwise + * the program fails. + * Most routines return pointers to static data that may change + * between calls to the same or other routines with a few exceptions: + * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask" + * return global static data. +*/ + +/* Christophe Burki wants `-p 6' to imply `-m tcp'. */ +/* Can't be zero. */ +static int +parse_rulenumber(const char *rule) +{ + unsigned int rulenum; + + if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Invalid rule number `%s'", rule); + + return rulenum; +} + +static const char * +parse_target(const char *targetname) +{ + const char *ptr; + + if (strlen(targetname) < 1) + xtables_error(PARAMETER_PROBLEM, + "Invalid target name (too short)"); + + if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN) + xtables_error(PARAMETER_PROBLEM, + "Invalid target name `%s' (%u chars max)", + targetname, XT_EXTENSION_MAXNAMELEN - 1); + + for (ptr = targetname; *ptr; ptr++) + if (isspace(*ptr)) + xtables_error(PARAMETER_PROBLEM, + "Invalid target name `%s'", targetname); + return targetname; +} + +static void +set_option(unsigned int *options, unsigned int option, uint8_t *invflg, + int invert) +{ + if (*options & option) + xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", + opt2char(option)); + *options |= option; + + if (invert) { + unsigned int i; + for (i = 0; 1 << i != option; i++); + + if (!inverse_for_options[i]) + xtables_error(PARAMETER_PROBLEM, + "cannot have ! before -%c", + opt2char(option)); + *invflg |= inverse_for_options[i]; + } +} + +static int +add_entry(const char *chain, + const char *table, + struct iptables_command_state *cs, + unsigned int nsaddrs, + const struct in_addr saddrs[], + const struct in_addr smasks[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + const struct in_addr dmasks[], + bool verbose, struct nft_handle *h, bool append) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + cs->fw.ip.src.s_addr = saddrs[i].s_addr; + cs->fw.ip.smsk.s_addr = smasks[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + cs->fw.ip.dst.s_addr = daddrs[j].s_addr; + cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; + + ret = nft_rule_add(h, chain, table, cs, append, verbose); + } + } + + return ret; +} + +static int +replace_entry(const char *chain, const char *table, + struct iptables_command_state *cs, + unsigned int rulenum, + const struct in_addr *saddr, const struct in_addr *smask, + const struct in_addr *daddr, const struct in_addr *dmask, + bool verbose, struct nft_handle *h) +{ + cs->fw.ip.src.s_addr = saddr->s_addr; + cs->fw.ip.dst.s_addr = daddr->s_addr; + cs->fw.ip.smsk.s_addr = smask->s_addr; + cs->fw.ip.dmsk.s_addr = dmask->s_addr; + + return nft_rule_replace(h, chain, table, cs, rulenum, verbose); +} + +static int +delete_entry(const char *chain, const char *table, + struct iptables_command_state *cs, + unsigned int nsaddrs, + const struct in_addr saddrs[], + const struct in_addr smasks[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + const struct in_addr dmasks[], + bool verbose, + struct nft_handle *h) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + cs->fw.ip.src.s_addr = saddrs[i].s_addr; + cs->fw.ip.smsk.s_addr = smasks[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + cs->fw.ip.dst.s_addr = daddrs[j].s_addr; + cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; + ret = nft_rule_delete(h, chain, table, cs, verbose); + } + } + + return ret; +} + +static int +check_entry(const char *chain, const char *table, + struct iptables_command_state *cs, + unsigned int nsaddrs, const struct in_addr *saddrs, + const struct in_addr *smasks, unsigned int ndaddrs, + const struct in_addr *daddrs, const struct in_addr *dmasks, + bool verbose, struct nft_handle *h) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + cs->fw.ip.src.s_addr = saddrs[i].s_addr; + cs->fw.ip.smsk.s_addr = smasks[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + cs->fw.ip.dst.s_addr = daddrs[j].s_addr; + cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; + ret = nft_rule_check(h, chain, table, cs, verbose); + } + } + + return ret; +} + +static int +zero_entries(const xt_chainlabel chain, int verbose, + struct xtc_handle *handle) +{ + /* XXX iterate over chains and reset counters */ + return 1; +} + +static int +list_entries(struct nft_handle *h, const char *chain, const char *table, + int rulenum, int verbose, int numeric, int expanded, + int linenumbers) +{ + unsigned int format; + + format = FMT_OPTIONS; + if (!verbose) + format |= FMT_NOCOUNTS; + else + format |= FMT_VIA; + + if (numeric) + format |= FMT_NUMERIC; + + if (!expanded) + format |= FMT_KILOMEGAGIGA; + + if (linenumbers) + format |= FMT_LINENUMBERS; + + /* FIXME should return found or not, and errno = ENOENT in such case */ + return nft_rule_list(h, chain, table, rulenum, format); +} + +static int +list_rules(struct nft_handle *h, const char *chain, const char *table, + int rulenum, int counters) +{ + if (counters) + counters = -1; /* iptables -c format */ + + nft_rule_list_save(h, chain, table, rulenum, counters); + + /* FIXME found */ + return 1; +} + +static void clear_rule_matches(struct xtables_rule_match **matches) +{ + struct xtables_rule_match *matchp, *tmp; + + for (matchp = *matches; matchp;) { + tmp = matchp->next; + if (matchp->match->m) { + free(matchp->match->m); + matchp->match->m = NULL; + } + if (matchp->match == matchp->match->next) { + free(matchp->match); + matchp->match = NULL; + } + free(matchp); + matchp = tmp; + } + + *matches = NULL; +} + +static void command_jump(struct iptables_command_state *cs) +{ + size_t size; + + set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags, cs->invert); + cs->jumpto = parse_target(optarg); + /* TRY_LOAD (may be chain name) */ + cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD); + + if (cs->target == NULL) + return; + + size = XT_ALIGN(sizeof(struct xt_entry_target)) + + cs->target->size; + + cs->target->t = xtables_calloc(1, size); + cs->target->t->u.target_size = size; + if (cs->target->real_name == NULL) { + strcpy(cs->target->t->u.user.name, cs->jumpto); + } else { + /* Alias support for userspace side */ + strcpy(cs->target->t->u.user.name, cs->target->real_name); + if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS)) + fprintf(stderr, "Notice: The %s target is converted into %s target " + "in rule listing and saving.\n", + cs->jumpto, cs->target->real_name); + } + cs->target->t->u.user.revision = cs->target->revision; + xs_init_target(cs->target); + + if (cs->target->x6_options != NULL) + opts = xtables_options_xfrm(iptables_globals.orig_opts, opts, + cs->target->x6_options, + &cs->target->option_offset); + else + opts = xtables_merge_options(iptables_globals.orig_opts, opts, + cs->target->extra_opts, + &cs->target->option_offset); + if (opts == NULL) + xtables_error(OTHER_PROBLEM, "can't alloc memory!"); +} + +static void command_match(struct iptables_command_state *cs) +{ + struct xtables_match *m; + size_t size; + + if (cs->invert) + xtables_error(PARAMETER_PROBLEM, + "unexpected ! flag before --match"); + + m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches); + size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size; + m->m = xtables_calloc(1, size); + m->m->u.match_size = size; + if (m->real_name == NULL) { + strcpy(m->m->u.user.name, m->name); + } else { + strcpy(m->m->u.user.name, m->real_name); + if (!(m->ext_flags & XTABLES_EXT_ALIAS)) + fprintf(stderr, "Notice: the %s match is converted into %s match " + "in rule listing and saving.\n", m->name, m->real_name); + } + m->m->u.user.revision = m->revision; + xs_init_match(m); + if (m == m->next) + return; + /* Merge options for non-cloned matches */ + if (m->x6_options != NULL) + opts = xtables_options_xfrm(iptables_globals.orig_opts, opts, + m->x6_options, &m->option_offset); + else if (m->extra_opts != NULL) + opts = xtables_merge_options(iptables_globals.orig_opts, opts, + m->extra_opts, &m->option_offset); + if (opts == NULL) + xtables_error(OTHER_PROBLEM, "can't alloc memory!"); +} + +int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) +{ + struct iptables_command_state cs; + unsigned int nsaddrs = 0, ndaddrs = 0; + struct in_addr *saddrs = NULL, *smasks = NULL; + struct in_addr *daddrs = NULL, *dmasks = NULL; + + int verbose = 0; + const char *chain = NULL; + const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; + const char *policy = NULL, *newname = NULL; + unsigned int rulenum = 0, command = 0; + const char *pcnt = NULL, *bcnt = NULL; + int ret = 1; + struct xtables_match *m; + struct xtables_rule_match *matchp; + struct xtables_target *t; + unsigned long long cnt; + + memset(&cs, 0, sizeof(cs)); + cs.jumpto = ""; + cs.argv = argv; + + /* re-set optind to 0 in case do_command4 gets called + * a second time */ + optind = 0; + + /* clear mflags in case do_command4 gets called a second time + * (we clear the global list of all matches for security)*/ + for (m = xtables_matches; m; m = m->next) + m->mflags = 0; + + for (t = xtables_targets; t; t = t->next) { + t->tflags = 0; + t->used = 0; + } + + /* Suppress error messages: we may add new options if we + demand-load a protocol. */ + opterr = 0; + + opts = xt_params->orig_opts; + while ((cs.c = getopt_long(argc, argv, + "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:46", + opts, NULL)) != -1) { + switch (cs.c) { + /* + * Command selection + */ + case 'A': + add_command(&command, CMD_APPEND, CMD_NONE, + cs.invert); + chain = optarg; + break; + + case 'C': + add_command(&command, CMD_CHECK, CMD_NONE, + cs.invert); + chain = optarg; + break; + + case 'D': + add_command(&command, CMD_DELETE, CMD_NONE, + cs.invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') { + rulenum = parse_rulenumber(argv[optind++]); + command = CMD_DELETE_NUM; + } + break; + + case 'R': + add_command(&command, CMD_REPLACE, CMD_NONE, + cs.invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires a rule number", + cmd2char(CMD_REPLACE)); + break; + + case 'I': + add_command(&command, CMD_INSERT, CMD_NONE, + cs.invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else rulenum = 1; + break; + + case 'L': + add_command(&command, CMD_LIST, + CMD_ZERO | CMD_ZERO_NUM, cs.invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + break; + + case 'S': + add_command(&command, CMD_LIST_RULES, + CMD_ZERO|CMD_ZERO_NUM, cs.invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + break; + + case 'F': + add_command(&command, CMD_FLUSH, CMD_NONE, + cs.invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'Z': + add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES, + cs.invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') { + rulenum = parse_rulenumber(argv[optind++]); + command = CMD_ZERO_NUM; + } + break; + + case 'N': + if (optarg && (*optarg == '-' || *optarg == '!')) + xtables_error(PARAMETER_PROBLEM, + "chain name not allowed to start " + "with `%c'\n", *optarg); + if (xtables_find_target(optarg, XTF_TRY_LOAD)) + xtables_error(PARAMETER_PROBLEM, + "chain name may not clash " + "with target name\n"); + add_command(&command, CMD_NEW_CHAIN, CMD_NONE, + cs.invert); + chain = optarg; + break; + + case 'X': + add_command(&command, CMD_DELETE_CHAIN, CMD_NONE, + cs.invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'E': + add_command(&command, CMD_RENAME_CHAIN, CMD_NONE, + cs.invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + newname = argv[optind++]; + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires old-chain-name and " + "new-chain-name", + cmd2char(CMD_RENAME_CHAIN)); + break; + + case 'P': + add_command(&command, CMD_SET_POLICY, CMD_NONE, + cs.invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + policy = argv[optind++]; + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires a chain and a policy", + cmd2char(CMD_SET_POLICY)); + break; + + case 'h': + if (!optarg) + optarg = argv[optind]; + + /* iptables -p icmp -h */ + if (!cs.matches && cs.protocol) + xtables_find_match(cs.protocol, + XTF_TRY_LOAD, &cs.matches); + + exit_printhelp(cs.matches); + + /* + * Option selection + */ + case 'p': + set_option(&cs.options, OPT_PROTOCOL, &cs.fw.ip.invflags, + cs.invert); + + /* Canonicalize into lower case */ + for (cs.protocol = optarg; *cs.protocol; cs.protocol++) + *cs.protocol = tolower(*cs.protocol); + + cs.protocol = optarg; + cs.fw.ip.proto = xtables_parse_protocol(cs.protocol); + + if (cs.fw.ip.proto == 0 + && (cs.fw.ip.invflags & XT_INV_PROTO)) + xtables_error(PARAMETER_PROBLEM, + "rule would never match protocol"); + break; + + case 's': + set_option(&cs.options, OPT_SOURCE, &cs.fw.ip.invflags, + cs.invert); + shostnetworkmask = optarg; + break; + + case 'd': + set_option(&cs.options, OPT_DESTINATION, &cs.fw.ip.invflags, + cs.invert); + dhostnetworkmask = optarg; + break; + +#ifdef IPT_F_GOTO + case 'g': + set_option(&cs.options, OPT_JUMP, &cs.fw.ip.invflags, + cs.invert); + cs.fw.ip.flags |= IPT_F_GOTO; + cs.jumpto = parse_target(optarg); + break; +#endif + + case 'j': + command_jump(&cs); + break; + + + case 'i': + if (*optarg == '\0') + xtables_error(PARAMETER_PROBLEM, + "Empty interface is likely to be " + "undesired"); + set_option(&cs.options, OPT_VIANAMEIN, &cs.fw.ip.invflags, + cs.invert); + xtables_parse_interface(optarg, + cs.fw.ip.iniface, + cs.fw.ip.iniface_mask); + break; + + case 'o': + if (*optarg == '\0') + xtables_error(PARAMETER_PROBLEM, + "Empty interface is likely to be " + "undesired"); + set_option(&cs.options, OPT_VIANAMEOUT, &cs.fw.ip.invflags, + cs.invert); + xtables_parse_interface(optarg, + cs.fw.ip.outiface, + cs.fw.ip.outiface_mask); + break; + + case 'f': + set_option(&cs.options, OPT_FRAGMENT, &cs.fw.ip.invflags, + cs.invert); + cs.fw.ip.flags |= IPT_F_FRAG; + break; + + case 'v': + if (!verbose) + set_option(&cs.options, OPT_VERBOSE, + &cs.fw.ip.invflags, cs.invert); + verbose++; + break; + + case 'm': + command_match(&cs); + break; + + case 'n': + set_option(&cs.options, OPT_NUMERIC, &cs.fw.ip.invflags, + cs.invert); + break; + + case 't': + if (cs.invert) + xtables_error(PARAMETER_PROBLEM, + "unexpected ! flag before --table"); + *table = optarg; + break; + + case 'x': + set_option(&cs.options, OPT_EXPANDED, &cs.fw.ip.invflags, + cs.invert); + break; + + case 'V': + if (cs.invert) + printf("Not %s ;-)\n", prog_vers); + else + printf("%s v%s\n", + prog_name, prog_vers); + exit(0); + + case '0': + set_option(&cs.options, OPT_LINENUMBERS, &cs.fw.ip.invflags, + cs.invert); + break; + + case 'M': + xtables_modprobe_program = optarg; + break; + + case 'c': + + set_option(&cs.options, OPT_COUNTERS, &cs.fw.ip.invflags, + cs.invert); + pcnt = optarg; + bcnt = strchr(pcnt + 1, ','); + if (bcnt) + bcnt++; + if (!bcnt && optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + bcnt = argv[optind++]; + if (!bcnt) + xtables_error(PARAMETER_PROBLEM, + "-%c requires packet and byte counter", + opt2char(OPT_COUNTERS)); + + if (sscanf(pcnt, "%llu", &cnt) != 1) + xtables_error(PARAMETER_PROBLEM, + "-%c packet counter not numeric", + opt2char(OPT_COUNTERS)); + cs.counters.pcnt = cnt; + + if (sscanf(bcnt, "%llu", &cnt) != 1) + xtables_error(PARAMETER_PROBLEM, + "-%c byte counter not numeric", + opt2char(OPT_COUNTERS)); + cs.counters.bcnt = cnt; + break; + + case '4': + /* This is indeed the IPv4 iptables */ + break; + + case '6': + /* This is not the IPv6 ip6tables */ + if (line != -1) + return 1; /* success: line ignored */ + fprintf(stderr, "This is the IPv4 version of iptables.\n"); + exit_tryhelp(2); + + case 1: /* non option */ + if (optarg[0] == '!' && optarg[1] == '\0') { + if (cs.invert) + xtables_error(PARAMETER_PROBLEM, + "multiple consecutive ! not" + " allowed"); + cs.invert = TRUE; + optarg[0] = '\0'; + continue; + } + fprintf(stderr, "Bad argument `%s'\n", optarg); + exit_tryhelp(2); + + default: + if (command_default(&cs, &iptables_globals) == 1) + /* cf. ip6tables.c */ + continue; + break; + } + cs.invert = FALSE; + } + + if (strcmp(*table, "nat") == 0 && + ((policy != NULL && strcmp(policy, "DROP") == 0) || + (cs.jumpto != NULL && strcmp(cs.jumpto, "DROP") == 0))) + xtables_error(PARAMETER_PROBLEM, + "\nThe \"nat\" table is not intended for filtering, " + "the use of DROP is therefore inhibited.\n\n"); + + for (matchp = cs.matches; matchp; matchp = matchp->next) + xtables_option_mfcall(matchp->match); + if (cs.target != NULL) + xtables_option_tfcall(cs.target); + + /* Fix me: must put inverse options checking here --MN */ + + if (optind < argc) + xtables_error(PARAMETER_PROBLEM, + "unknown arguments found on commandline"); + if (!command) + xtables_error(PARAMETER_PROBLEM, "no command specified"); + if (cs.invert) + xtables_error(PARAMETER_PROBLEM, + "nothing appropriate following !"); + + if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs.options & OPT_DESTINATION)) + dhostnetworkmask = "0.0.0.0/0"; + if (!(cs.options & OPT_SOURCE)) + shostnetworkmask = "0.0.0.0/0"; + } + + if (shostnetworkmask) + xtables_ipparse_multiple(shostnetworkmask, &saddrs, + &smasks, &nsaddrs); + + if (dhostnetworkmask) + xtables_ipparse_multiple(dhostnetworkmask, &daddrs, + &dmasks, &ndaddrs); + + if ((nsaddrs > 1 || ndaddrs > 1) && + (cs.fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple" + " source or destination IP addresses"); + + if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) + xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " + "specify a unique address"); + + generic_opt_check(command, cs.options); + + if (chain != NULL && strlen(chain) >= XT_EXTENSION_MAXNAMELEN) + xtables_error(PARAMETER_PROBLEM, + "chain name `%s' too long (must be under %u chars)", + chain, XT_EXTENSION_MAXNAMELEN); + + if (command == CMD_APPEND + || command == CMD_DELETE + || command == CMD_CHECK + || command == CMD_INSERT + || command == CMD_REPLACE) { + if (strcmp(chain, "PREROUTING") == 0 + || strcmp(chain, "INPUT") == 0) { + /* -o not valid with incoming packets. */ + if (cs.options & OPT_VIANAMEOUT) + xtables_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEOUT), + chain); + } + + if (strcmp(chain, "POSTROUTING") == 0 + || strcmp(chain, "OUTPUT") == 0) { + /* -i not valid with outgoing packets */ + if (cs.options & OPT_VIANAMEIN) + xtables_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEIN), + chain); + } + + /* + * Contrary to what iptables does, we assume that any jumpto + * is a custom chain jumps (if no target is found). Later on, + * nf_table will spot the error if the chain does not exists. + */ + } + + switch (command) { + case CMD_APPEND: + ret = add_entry(chain, *table, &cs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, + cs.options&OPT_VERBOSE, + h, true); + break; + case CMD_DELETE: + ret = delete_entry(chain, *table, &cs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, + cs.options&OPT_VERBOSE, h); + break; + case CMD_DELETE_NUM: + ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose); + break; + case CMD_CHECK: + ret = check_entry(chain, *table, &cs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, + cs.options&OPT_VERBOSE, h); + break; + case CMD_REPLACE: + /* FIXME replace at rulenum */ + ret = replace_entry(chain, *table, &cs, rulenum - 1, + saddrs, smasks, daddrs, dmasks, + cs.options&OPT_VERBOSE, h); + break; + case CMD_INSERT: + /* FIXME insert at rulenum */ + ret = add_entry(chain, *table, &cs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, + cs.options&OPT_VERBOSE, h, false); + break; + case CMD_FLUSH: + ret = nft_rule_flush(h, chain, *table); + break; + case CMD_ZERO: + /* FIXME */ +// ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle); + break; + case CMD_ZERO_NUM: + /* FIXME */ +// ret = iptc_zero_counter(chain, rulenum, *handle); + break; + case CMD_LIST: + case CMD_LIST|CMD_ZERO: + case CMD_LIST|CMD_ZERO_NUM: + /* FIXME */ + ret = list_entries(h, chain, *table, + rulenum, + cs.options&OPT_VERBOSE, + cs.options&OPT_NUMERIC, + cs.options&OPT_EXPANDED, + cs.options&OPT_LINENUMBERS); +/* if (ret && (command & CMD_ZERO)) + ret = zero_entries(chain, + cs.options&OPT_VERBOSE, *handle); + if (ret && (command & CMD_ZERO_NUM)) + ret = iptc_zero_counter(chain, rulenum, *handle); */ + break; + case CMD_LIST_RULES: + case CMD_LIST_RULES|CMD_ZERO: + case CMD_LIST_RULES|CMD_ZERO_NUM: + /* FIXME */ + ret = list_rules(h, chain, *table, rulenum, cs.options&OPT_VERBOSE); +/* if (ret && (command & CMD_ZERO)) + ret = zero_entries(chain, + cs.options&OPT_VERBOSE, *handle); + if (ret && (command & CMD_ZERO_NUM)) + ret = iptc_zero_counter(chain, rulenum, *handle); */ + break; + case CMD_NEW_CHAIN: + ret = nft_chain_user_add(h, chain, *table); + break; + case CMD_DELETE_CHAIN: + ret = nft_chain_user_del(h, chain, *table); + break; + case CMD_RENAME_CHAIN: + /* XXX iptables allows renaming an used chain, we don't */ + ret = nft_chain_user_rename(h, chain, *table, newname); + break; + case CMD_SET_POLICY: + ret = nft_chain_set(h, *table, chain, policy, NULL); + if (ret < 0) + xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n", + policy); + break; + default: + /* We should never reach this... */ + exit_tryhelp(2); + } + +/* if (verbose > 1) + dump_entries(*handle); */ + + clear_rule_matches(&cs.matches); + + free(saddrs); + free(smasks); + free(daddrs); + free(dmasks); + xtables_free_opts(1); + + return ret; +} diff --git a/libxtables/xtables.c b/libxtables/xtables.c index fb60c01b..a511c08c 100644 --- a/libxtables/xtables.c +++ b/libxtables/xtables.c @@ -743,7 +743,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) return ptr; } -static int compatible_revision(const char *name, uint8_t revision, int opt) +int xtables_compatible_revision(const char *name, uint8_t revision, int opt) { struct xt_get_revision rev; socklen_t s = sizeof(rev); @@ -799,12 +799,12 @@ static int compatible_revision(const char *name, uint8_t revision, int opt) static int compatible_match_revision(const char *name, uint8_t revision) { - return compatible_revision(name, revision, afinfo->so_rev_match); + return xt_params->compat_rev(name, revision, afinfo->so_rev_match); } static int compatible_target_revision(const char *name, uint8_t revision) { - return compatible_revision(name, revision, afinfo->so_rev_target); + return xt_params->compat_rev(name, revision, afinfo->so_rev_target); } static void xtables_check_options(const char *name, const struct option *opt) -- cgit v1.2.3 From c74f398c58e7b77ca1c82a15d0b2e9eed4fb82ad Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 24 Oct 2012 13:40:12 +0200 Subject: automatic creation of built-in table and chains In order to emulate the iptables behaviour, this patch changes the current behaviour to: 1st) check if the table and chains are built-in. 2nd) If they don't exists, create them. If they exists, don't touch them. The automatic creation happens in the -I and -P paths. We should provide a new command to allow to delete (unregister) built-in tables and chains. It would be similar to unloading the iptable_X module that registers the custom table. This is not done for other commands like -C or -D since they will fail while trying to find the rule in the kernel if such combination of chain and table does not exists. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 143 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 105 insertions(+), 38 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 91383bfb..9eaa2ac1 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -82,7 +82,7 @@ static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, return 0; } -static void nft_table_init_one(struct nft_handle *h, const char *name, int portid) +static void nft_table_builtin_add(struct nft_handle *h, const char *table) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -92,7 +92,7 @@ static void nft_table_init_one(struct nft_handle *h, const char *name, int porti if (t == NULL) return; - nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)name); + nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table); nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, NLM_F_ACK|NLM_F_EXCL, h->seq); @@ -111,7 +111,7 @@ static void nft_table_init_one(struct nft_handle *h, const char *name, int porti #define SECURITY 3 #define TABLES_MAX 4 -static struct default_table { +static struct builtin_table { const char *name; uint32_t prio; } tables[TABLES_MAX] = { @@ -134,15 +134,7 @@ static struct default_table { /* nat already registered by nf_tables */ }; -static void nft_table_init(struct nft_handle *h) -{ - int i; - - for (i=0; iportid); -} - -static struct default_chain { +static struct builtin_chain { const char *name; uint32_t hook; } chains[TABLES_MAX][NF_IP_NUMHOOKS] = { @@ -208,17 +200,15 @@ static struct default_chain { }, }; -static void -nft_chain_default_add(struct nft_handle *h, struct default_table *table, - struct default_chain *chain, int policy) +static struct nft_chain * +nft_chain_builtin_alloc(struct builtin_table *table, + struct builtin_chain *chain, int policy) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; struct nft_chain *c; c = nft_chain_alloc(); if (c == NULL) - return; + return NULL; nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table->name); nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain->name); @@ -226,6 +216,21 @@ nft_chain_default_add(struct nft_handle *h, struct default_table *table, nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_PRIO, table->prio); nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy); + return c; +} + +static void +nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, + struct builtin_chain *chain, int policy) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_chain *c; + + c = nft_chain_builtin_alloc(table, chain, policy); + if (c == NULL) + return; + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -233,23 +238,72 @@ nft_chain_default_add(struct nft_handle *h, struct default_table *table, if (mnl_talk(h, nlh, NULL, NULL) < 0) { if (errno != EEXIST) - perror("mnl_talk:nft_chain_default_add"); + perror("mnl_talk:nft_chain_builtin_add"); } } -static void nft_chain_init(struct nft_handle *h) +/* find if built-in table already exists */ +static struct builtin_table *nft_table_builtin_find(const char *table) +{ + int i; + bool found = false; + + for (i=0; iportid = mnl_socket_get_portid(h->nl); -/* - * In case we want to inconditionally register all tables / chain. - * This is not flexible and performance consuming. - * - * nft_table_init(h); - * nft_chain_init(h); - */ - return 0; } @@ -326,14 +372,32 @@ __nft_chain_set(struct nft_handle *h, const char *table, struct nlmsghdr *nlh; struct nft_chain *c; int ret; + struct builtin_table *_t; + struct builtin_chain *_c; - c = nft_chain_alloc(); - if (c == NULL) - return -1; + _t = nft_table_builtin_find(table); + /* if this built-in table does not exists, create it */ + if (_t != NULL) + nft_table_builtin_add(h, table); + + _c = nft_chain_builtin_find(table, chain); + if (_c != NULL) { + /* This is a built-in chain */ + c = nft_chain_builtin_alloc(_t, _c, policy); + if (c == NULL) + return -1; + + } else { + /* This is a custom chain */ + c = nft_chain_alloc(); + if (c == NULL) + return -1; + + nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); + nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); + nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy); + } - nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); - nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); - nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy); if (counters) { nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_BYTES, counters->bcnt); @@ -546,6 +610,9 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, uint32_t op; int flags = append ? NLM_F_APPEND : 0; + /* If built-in chain does not exists, create it */ + nft_chain_builtin_init(h, table, chain, NF_ACCEPT); + nft_fn = nft_rule_add; r = nft_rule_alloc(); -- cgit v1.2.3 From e17b5f15c2beda86565a8f4e55fdf12ceb20dd59 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 29 Oct 2012 11:39:46 +0100 Subject: rework automatic creation of built-in table and chains This patch reworks the automatic creation of built-in table and chains. Now it initializes all built-in chains belonging a table at once. This happens with commands: -P, -A, -I, -N Note that xtables skips chain initialization if it notices that the table already exists in the kernel. Thanks to Tomasz Bursztyka for spotting problems with -N. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 205 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 112 insertions(+), 93 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 9eaa2ac1..1cd432af 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -82,15 +82,16 @@ static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, return 0; } -static void nft_table_builtin_add(struct nft_handle *h, const char *table) +static int nft_table_builtin_add(struct nft_handle *h, const char *table) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; struct nft_table *t; + int ret; t = nft_table_alloc(); if (t == NULL) - return; + return -1; nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table); @@ -99,10 +100,12 @@ static void nft_table_builtin_add(struct nft_handle *h, const char *table) nft_table_nlmsg_build_payload(nlh, t); nft_table_free(t); - if (mnl_talk(h, nlh, NULL, NULL) < 0) { + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) { if (errno != EEXIST) perror("mnl-talk:nft_table_init_one"); } + return ret; } #define FILTER 0 @@ -111,93 +114,93 @@ static void nft_table_builtin_add(struct nft_handle *h, const char *table) #define SECURITY 3 #define TABLES_MAX 4 +struct builtin_chain { + const char *name; + uint32_t hook; +}; + static struct builtin_table { const char *name; uint32_t prio; + struct builtin_chain chains[NF_INET_NUMHOOKS]; } tables[TABLES_MAX] = { [RAW] = { .name = "raw", .prio = -300, /* NF_IP_PRI_RAW */ + .chains = { + { + .name = "PREROUTING", + .hook = NF_INET_PRE_ROUTING, + }, + { + .name = "OUTPUT", + .hook = NF_INET_LOCAL_OUT, + }, + }, }, [MANGLE] = { .name = "mangle", .prio = -150, /* NF_IP_PRI_MANGLE */ + .chains = { + { + .name = "PREROUTING", + .hook = NF_INET_PRE_ROUTING, + }, + { + .name = "INPUT", + .hook = NF_INET_LOCAL_IN, + }, + { + .name = "FORWARD", + .hook = NF_INET_FORWARD, + }, + { + .name = "OUTPUT", + .hook = NF_INET_LOCAL_OUT, + }, + { + .name = "POSTROUTING", + .hook = NF_INET_POST_ROUTING, + }, + }, }, [FILTER] = { .name = "filter", .prio = 0, /* NF_IP_PRI_FILTER */ + .chains = { + { + .name = "INPUT", + .hook = NF_INET_LOCAL_IN, + }, + { + .name = "FORWARD", + .hook = NF_INET_FORWARD, + }, + { + .name = "OUTPUT", + .hook = NF_INET_LOCAL_OUT, + }, + }, }, [SECURITY] = { .name = "security", .prio = 150, /* NF_IP_PRI_SECURITY */ - }, - /* nat already registered by nf_tables */ -}; - -static struct builtin_chain { - const char *name; - uint32_t hook; -} chains[TABLES_MAX][NF_IP_NUMHOOKS] = { - [FILTER] = { - { - .name = "INPUT", - .hook = NF_INET_LOCAL_IN, - }, - { - .name = "FORWARD", - .hook = NF_INET_FORWARD, - }, - { - .name = "OUTPUT", - .hook = NF_INET_LOCAL_OUT, - }, - }, - [MANGLE] = { - { - .name = "PREROUTING", - .hook = NF_INET_PRE_ROUTING, - }, - { - .name = "INPUT", - .hook = NF_INET_LOCAL_IN, - }, - { - .name = "FORWARD", - .hook = NF_INET_FORWARD, - }, - { - .name = "OUTPUT", - .hook = NF_INET_LOCAL_OUT, - }, - { - .name = "POSTROUTING", - .hook = NF_INET_POST_ROUTING, - }, - }, - [RAW] = { - { - .name = "PREROUTING", - .hook = NF_INET_PRE_ROUTING, - }, - { - .name = "OUTPUT", - .hook = NF_INET_LOCAL_OUT, - }, - }, - [SECURITY] = { - { - .name = "INPUT", - .hook = NF_INET_LOCAL_IN, - }, - { - .name = "FORWARD", - .hook = NF_INET_FORWARD, - }, - { - .name = "OUTPUT", - .hook = NF_INET_LOCAL_OUT, + .chains = { + { + .name = "INPUT", + .hook = NF_INET_LOCAL_IN, + }, + { + .name = "FORWARD", + .hook = NF_INET_FORWARD, + }, + { + .name = "OUTPUT", + .hook = NF_INET_LOCAL_OUT, + }, }, }, + /* nat already registered by nf_tables */ }; static struct nft_chain * @@ -261,23 +264,40 @@ static struct builtin_table *nft_table_builtin_find(const char *table) /* find if built-in chain already exists */ static struct builtin_chain * -nft_chain_builtin_find(const char *table, const char *chain) +nft_chain_builtin_find(struct builtin_table *t, const char *chain) { - int i, j; + int i; bool found = false; - for (i=0; ichains[i].name != NULL; i++) { + if (strcmp(t->chains[i].name, chain) != 0) + continue; - found = true; - goto out; - } + found = true; + break; + } + return found ? &t->chains[i] : NULL; +} + +static void +__nft_chain_builtin_init(struct nft_handle *h, + struct builtin_table *table, const char *chain, + int policy) +{ + int i, default_policy; + + /* Initialize all built-in chains. Exception, for e one received as + * parameter, set the default policy as requested. + */ + for (i=0; ichains[i].name != NULL; i++) { + if (chain && strcmp(table->chains[i].name, chain) == 0) + default_policy = policy; + else + default_policy = NF_ACCEPT; + + nft_chain_builtin_add(h, table, &table->chains[i], + default_policy); } -out: - return found ? &chains[i][j] : NULL; } static int @@ -286,22 +306,18 @@ nft_chain_builtin_init(struct nft_handle *h, const char *table, { int ret = 0; struct builtin_table *t; - struct builtin_chain *c; t = nft_table_builtin_find(table); if (t == NULL) { ret = -1; goto out; } - nft_table_builtin_add(h, table); - - c = nft_chain_builtin_find(table, chain); - if (c == NULL) { - ret = -1; - goto out; + if (nft_table_builtin_add(h, table) < 0) { + /* Built-in table already initialized, skip. */ + if (errno == EEXIST) + goto out; } - /* If it already exists, it does not modify it */ - nft_chain_builtin_add(h, t, c, policy); + __nft_chain_builtin_init(h, t, chain, policy); out: return ret; } @@ -371,16 +387,16 @@ __nft_chain_set(struct nft_handle *h, const char *table, char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; struct nft_chain *c; - int ret; struct builtin_table *_t; struct builtin_chain *_c; + int ret; _t = nft_table_builtin_find(table); /* if this built-in table does not exists, create it */ if (_t != NULL) nft_table_builtin_add(h, table); - _c = nft_chain_builtin_find(table, chain); + _c = nft_chain_builtin_find(_t, chain); if (_c != NULL) { /* This is a built-in chain */ c = nft_chain_builtin_alloc(_t, _c, policy); @@ -610,7 +626,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, uint32_t op; int flags = append ? NLM_F_APPEND : 0; - /* If built-in chain does not exists, create it */ + /* If built-in chains don't exist for this table, create them */ nft_chain_builtin_init(h, table, chain, NF_ACCEPT); nft_fn = nft_rule_add; @@ -1364,6 +1380,9 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl struct nft_chain *c; int ret; + /* If built-in chains don't exist for this table, create them */ + nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + c = nft_chain_alloc(); if (c == NULL) { DEBUGP("cannot allocate chain\n"); -- cgit v1.2.3 From 2f1fbab67157624c0171e55bde08e7e5c8d99fff Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 29 Oct 2012 22:52:50 +0100 Subject: iptables: nft: add -f support Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index 1cd432af..0369461a 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -580,6 +580,24 @@ static void add_payload(struct nft_rule *r, int offset, int len) nft_rule_add_expr(r, expr); } +/* bitwise operation is = sreg & mask ^ xor */ +static void add_bitwise_u16(struct nft_rule *r, int mask, int xor) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("bitwise"); + if (expr == NULL) + return; + + nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_SREG, NFT_REG_1); + nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_DREG, NFT_REG_1); + nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_LEN, sizeof(uint16_t)); + nft_rule_expr_set(expr, NFT_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t)); + nft_rule_expr_set(expr, NFT_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t)); + + nft_rule_add_expr(r, expr); +} + static void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len) { struct nft_rule_expr *expr; @@ -595,6 +613,11 @@ static void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len) nft_rule_add_expr(r, expr); } +static void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op) +{ + add_cmp_ptr(r, op, &val, sizeof(val)); +} + static void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op) { add_cmp_ptr(r, op, &val, sizeof(val)); @@ -699,6 +722,19 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, add_cmp_u32(r, cs->fw.ip.proto, op); } + if (cs->fw.ip.flags & IPT_F_FRAG) { + add_payload(r, offsetof(struct iphdr, frag_off), 2); + /* get the 13 bits that contain the fragment offset */ + add_bitwise_u16(r, 0x1fff, !0x1fff); + + /* if offset is non-zero, this is a fragment */ + if (cs->fw.ip.invflags & IPT_INV_FRAG) + op = NFT_CMP_EQ; + else + op = NFT_CMP_NEQ; + + add_cmp_u16(r, 0, op); + } for (matchp = cs->matches; matchp; matchp = matchp->next) add_match(r, matchp->match->m); @@ -970,6 +1006,50 @@ get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv *inv = false; } +static void get_frag(struct nft_rule_expr_iter *iter, bool *inv) +{ + struct nft_rule_expr *e; + const char *name; + uint8_t op; + + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + /* we assume correct mask and xor */ + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + if (strcmp(name, "bitwise") != 0) { + DEBUGP("skipping no bitwise after payload\n"); + return; + } + + /* Now check for cmp */ + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + /* we assume correct data */ + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + if (strcmp(name, "cmp") != 0) { + DEBUGP("skipping no cmp after payload\n"); + return; + } + + op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); + if (op == NFT_CMP_EQ) + *inv = true; + else + *inv = false; +} + +static void print_frag(bool inv) +{ + if (inv) + printf("! -f "); + else + printf("-f "); +} + static void print_proto(uint16_t proto, int invert) { const struct protoent *pent = getprotobynumber(proto); @@ -1045,6 +1125,10 @@ nft_print_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) get_cmp_data(iter, &proto, sizeof(proto), &inv); print_proto(proto, inv); break; + case offsetof(struct iphdr, frag_off): + get_frag(iter, &inv); + print_frag(inv); + break; default: DEBUGP("unknown payload offset %d\n", offset); break; @@ -1803,6 +1887,12 @@ nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, if (inv) cs->fw.ip.invflags |= IPT_INV_PROTO; break; + case offsetof(struct iphdr, frag_off): + cs->fw.ip.flags |= IPT_F_FRAG; + get_frag(iter, &inv); + if (inv) + cs->fw.ip.invflags |= IPT_INV_FRAG; + break; default: DEBUGP("unknown payload offset %d\n", offset); break; -- cgit v1.2.3 From 1373cbb1068a1dc57540faebcf2f5f0fcbe83b53 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 30 Oct 2012 23:31:04 +0000 Subject: headers: Make nf_tables.h up to date Signed-off-by: Tomasz Bursztyka --- include/linux/netfilter/nf_tables.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index a6d51374..74a521aa 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -65,6 +65,8 @@ enum nft_chain_attributes { NFTA_CHAIN_TABLE, NFTA_CHAIN_NAME, NFTA_CHAIN_HOOK, + NFTA_CHAIN_POLICY, + NFTA_CHAIN_USE, __NFTA_CHAIN_MAX }; #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) -- cgit v1.2.3 From 4493582ccb60a443fc7efeca78edafbefc689aa3 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 30 Oct 2012 23:31:05 +0000 Subject: nft: Add support for chain rename options (-E) Signed-off-by: Tomasz Bursztyka --- include/linux/netfilter/nf_tables.h | 1 + iptables/nft.c | 33 ++++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 74a521aa..63480b32 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -67,6 +67,7 @@ enum nft_chain_attributes { NFTA_CHAIN_HOOK, NFTA_CHAIN_POLICY, NFTA_CHAIN_USE, + NFTA_CHAIN_NEW_NAME, __NFTA_CHAIN_MAX }; #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) diff --git a/iptables/nft.c b/iptables/nft.c index 0369461a..5764ec87 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1576,14 +1576,37 @@ err: int nft_chain_user_rename(struct nft_handle *h,const char *chain, const char *table, const char *newname) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_chain *c; int ret; - /* XXX need new operation in nf_tables to support this */ - ret = nft_chain_user_del(h, chain, table); - if (ret < 0) - return ret; + /* If built-in chains don't exist for this table, create them */ + nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); - return nft_chain_user_add(h, newname, table); + c = nft_chain_alloc(); + if (c == NULL) { + DEBUGP("cannot allocate chain\n"); + return -1; + } + + nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); + nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); + nft_chain_attr_set(c, NFT_CHAIN_ATTR_NEW_NAME, (char *)newname); + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + NLM_F_ACK|NLM_F_REPLACE, h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + nft_chain_free(c); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) { + if (errno != EEXIST) + perror("mnl_talk:nft_chain_rename"); + } + + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; } static int nft_table_list_cb(const struct nlmsghdr *nlh, void *data) -- cgit v1.2.3 From 4acee778f5712c4cc574e328183a3252ad81a802 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 30 Oct 2012 23:31:06 +0000 Subject: iptables: nft: Fix -D chain rulenum option Signed-off-by: Tomasz Bursztyka --- iptables/nft.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 5764ec87..dcc9ec4b 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2237,10 +2237,10 @@ __nft_rule_check(struct nft_handle *h, const char *chain, const char *table, if (rulenum >= 0) { /* Delete by rule number case */ - if (rule_ctr != rulenum) { - rule_ctr++; + if (rule_ctr != rulenum) goto next; - } + found = true; + break; } else { /* Delete by matching rule case */ DEBUGP("comparing with... "); @@ -2272,6 +2272,7 @@ __nft_rule_check(struct nft_handle *h, const char *chain, const char *table, break; } next: + rule_ctr++; r = nft_rule_list_iter_next(iter); } -- cgit v1.2.3 From 16db62f9187ec1d62310efea8f0f7f378aee1e89 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 30 Oct 2012 23:31:07 +0000 Subject: iptables: nft: Refactor __nft_rule_check to return rule handle when relevant Signed-off-by: Tomasz Bursztyka --- iptables/nft.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index dcc9ec4b..ab9e4e40 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2200,7 +2200,7 @@ __nft_rule_del(struct nft_handle *h, struct nft_rule *r) static int __nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs, - bool delete, int rulenum, bool verbose) + bool delete, bool replace, int rulenum, bool verbose) { struct nft_rule_list *list; struct nft_rule_list_iter *iter; @@ -2282,7 +2282,8 @@ next: if (delete) { DEBUGP("deleting rule\n"); __nft_rule_del(h, r); - } + } else if (replace) + ret = nft_rule_attr_get_u16(r, NFT_RULE_ATTR_HANDLE); } nft_rule_list_iter_destroy(iter); @@ -2300,7 +2301,7 @@ int nft_rule_check(struct nft_handle *h, const char *chain, { nft_fn = nft_rule_check; - return __nft_rule_check(h, chain, table, e, false, -1, verbose); + return __nft_rule_check(h, chain, table, e, false, false, -1, verbose); } int nft_rule_delete(struct nft_handle *h, const char *chain, @@ -2309,7 +2310,7 @@ int nft_rule_delete(struct nft_handle *h, const char *chain, { nft_fn = nft_rule_delete; - return __nft_rule_check(h, chain, table, e, true, -1, verbose); + return __nft_rule_check(h, chain, table, e, true, false, -1, verbose); } int nft_rule_delete_num(struct nft_handle *h, const char *chain, @@ -2318,7 +2319,8 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain, { nft_fn = nft_rule_delete_num; - return __nft_rule_check(h, chain, table, NULL, true, rulenum, verbose); + return __nft_rule_check(h, chain, table, + NULL, true, false, rulenum, verbose); } int nft_rule_replace(struct nft_handle *h, const char *chain, @@ -2329,7 +2331,8 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, nft_fn = nft_rule_replace; - ret = __nft_rule_check(h, chain, table, NULL, true, rulenum, verbose); + ret = __nft_rule_check(h, chain, table, + NULL, false, true, rulenum, verbose); if (ret < 0) return ret; -- cgit v1.2.3 From 0a366d8696582e979d55f6832a797d1217f4b908 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 30 Oct 2012 23:31:08 +0000 Subject: iptables: nft: Add support for -R option Signed-off-by: Tomasz Bursztyka --- iptables/nft.c | 28 ++++++++++++++++++---------- iptables/nft.h | 2 +- iptables/xtables.c | 3 ++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index ab9e4e40..3b5a5c1e 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -639,7 +639,8 @@ static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, - struct iptables_command_state *cs, bool append, bool verbose) + struct iptables_command_state *cs, + bool append, uint16_t handle, bool verbose) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -764,8 +765,16 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, } /* NLM_F_CREATE autoloads the built-in table if it does not exists */ - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, AF_INET, - NLM_F_ACK|NLM_F_CREATE|flags, h->seq); + flags |= NLM_F_ACK|NLM_F_CREATE; + + if (handle > 0) { + nft_rule_attr_set(r, NFT_RULE_ATTR_HANDLE, &handle); + flags |= NLM_F_REPLACE; + } + + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, + AF_INET, flags, h->seq); + nft_rule_nlmsg_build_payload(nlh, r); nft_rule_print_debug(r, nlh); @@ -2327,17 +2336,16 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs, int rulenum, bool verbose) { - int ret; + int handle; nft_fn = nft_rule_replace; - ret = __nft_rule_check(h, chain, table, - NULL, false, true, rulenum, verbose); - if (ret < 0) - return ret; + handle = __nft_rule_check(h, chain, table, + NULL, false, true, rulenum, verbose); + if (handle < 0) + return handle; - /* XXX needs to be inserted in position, this is appending */ - return nft_rule_add(h, chain, table, cs, true, verbose); + return nft_rule_add(h, chain, table, cs, true, handle, verbose); } /* diff --git a/iptables/nft.h b/iptables/nft.h index f5a9efb8..474e652e 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -39,7 +39,7 @@ int nft_chain_user_rename(struct nft_handle *h, const char *chain, const char *t */ struct nft_rule; -int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool append, bool verbose); +int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool append, uint16_t handle, bool verbose); int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose); diff --git a/iptables/xtables.c b/iptables/xtables.c index a6875757..d1a91723 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -448,7 +448,8 @@ add_entry(const char *chain, cs->fw.ip.dst.s_addr = daddrs[j].s_addr; cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; - ret = nft_rule_add(h, chain, table, cs, append, verbose); + ret = nft_rule_add(h, chain, table, + cs, append, 0, verbose); } } -- cgit v1.2.3 From 06fc595fa99ab0036d87b259b0d20e4916522969 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 1 Nov 2012 16:26:46 +0100 Subject: nft: fix missing rule listing in custom chains with -L Reported-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 3b5a5c1e..c803ffef 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2757,11 +2757,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, print_header(format, chain_name, policy_name[policy], &ctrs, basechain, refs); - /* this is a base chain */ - if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) { - __nft_rule_list(h, c, table, rulenum, format, - print_firewall); - } + __nft_rule_list(h, c, table, rulenum, format, print_firewall); next: c = nft_chain_list_iter_next(iter); } -- cgit v1.2.3 From 42239cb39ecf7650fefb5cc301ab943be11de6fb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 3 Nov 2012 11:09:30 +0100 Subject: headers: remove unused compatibility definitions They belong to nf_tables_compat.h Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 63480b32..542b6549 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -400,22 +400,4 @@ enum nft_nat_attributes { }; #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) -enum nft_target_attributes { - NFTA_TARGET_UNSPEC, - NFTA_TARGET_NAME, - NFTA_TARGET_REV, - NFTA_TARGET_INFO, - __NFTA_TARGET_MAX -}; -#define NFTA_TARGET_MAX (__NFTA_TARGET_MAX - 1) - -enum nft_match_attributes { - NFTA_MATCH_UNSPEC, - NFTA_MATCH_NAME, - NFTA_MATCH_REV, - NFTA_MATCH_INFO, - __NFTA_MATCH_MAX -}; -#define NFTA_MATCH_MAX (__NFTA_MATCH_MAX - 1) - #endif /* _LINUX_NF_TABLES_H */ -- cgit v1.2.3 From 5b414b85c33912aec912d260502c8b0c0df794a1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 3 Nov 2012 11:12:45 +0100 Subject: iptables: nft: move priority to chain instead of table NAT table uses different chain priorities, adapt the existing code to allow this. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 77 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index c803ffef..0d07aa51 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -82,32 +82,6 @@ static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, return 0; } -static int nft_table_builtin_add(struct nft_handle *h, const char *table) -{ - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; - struct nft_table *t; - int ret; - - t = nft_table_alloc(); - if (t == NULL) - return -1; - - nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table); - - nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, - NLM_F_ACK|NLM_F_EXCL, h->seq); - nft_table_nlmsg_build_payload(nlh, t); - nft_table_free(t); - - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) { - if (errno != EEXIST) - perror("mnl-talk:nft_table_init_one"); - } - return ret; -} - #define FILTER 0 #define MANGLE 1 #define RAW 2 @@ -116,86 +90,95 @@ static int nft_table_builtin_add(struct nft_handle *h, const char *table) struct builtin_chain { const char *name; + uint32_t prio; uint32_t hook; }; static struct builtin_table { const char *name; - uint32_t prio; struct builtin_chain chains[NF_INET_NUMHOOKS]; } tables[TABLES_MAX] = { [RAW] = { .name = "raw", - .prio = -300, /* NF_IP_PRI_RAW */ .chains = { { .name = "PREROUTING", + .prio = -300, /* NF_IP_PRI_RAW */ .hook = NF_INET_PRE_ROUTING, }, { .name = "OUTPUT", + .prio = -300, /* NF_IP_PRI_RAW */ .hook = NF_INET_LOCAL_OUT, }, }, }, [MANGLE] = { .name = "mangle", - .prio = -150, /* NF_IP_PRI_MANGLE */ .chains = { { .name = "PREROUTING", + .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_PRE_ROUTING, }, { .name = "INPUT", + .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_LOCAL_IN, }, { .name = "FORWARD", + .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_FORWARD, }, { .name = "OUTPUT", + .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_LOCAL_OUT, }, { .name = "POSTROUTING", + .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_POST_ROUTING, }, }, }, [FILTER] = { .name = "filter", - .prio = 0, /* NF_IP_PRI_FILTER */ .chains = { { .name = "INPUT", + .prio = 0, /* NF_IP_PRI_FILTER */ .hook = NF_INET_LOCAL_IN, }, { .name = "FORWARD", + .prio = 0, /* NF_IP_PRI_FILTER */ .hook = NF_INET_FORWARD, }, { .name = "OUTPUT", + .prio = 0, /* NF_IP_PRI_FILTER */ .hook = NF_INET_LOCAL_OUT, }, }, }, [SECURITY] = { .name = "security", - .prio = 150, /* NF_IP_PRI_SECURITY */ .chains = { { .name = "INPUT", + .prio = 150, /* NF_IP_PRI_SECURITY */ .hook = NF_INET_LOCAL_IN, }, { .name = "FORWARD", + .prio = 150, /* NF_IP_PRI_SECURITY */ .hook = NF_INET_FORWARD, }, { .name = "OUTPUT", + .prio = 150, /* NF_IP_PRI_SECURITY */ .hook = NF_INET_LOCAL_OUT, }, }, @@ -203,6 +186,32 @@ static struct builtin_table { /* nat already registered by nf_tables */ }; +static int nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_table *t; + int ret; + + t = nft_table_alloc(); + if (t == NULL) + return -1; + + nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)_t->name); + + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + NLM_F_ACK|NLM_F_EXCL, h->seq); + nft_table_nlmsg_build_payload(nlh, t); + nft_table_free(t); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) { + if (errno != EEXIST) + perror("mnl-talk:nft_table_init_one"); + } + return ret; +} + static struct nft_chain * nft_chain_builtin_alloc(struct builtin_table *table, struct builtin_chain *chain, int policy) @@ -216,7 +225,7 @@ nft_chain_builtin_alloc(struct builtin_table *table, nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table->name); nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain->name); nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_HOOKNUM, chain->hook); - nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_PRIO, table->prio); + nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_PRIO, chain->prio); nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy); return c; @@ -312,7 +321,7 @@ nft_chain_builtin_init(struct nft_handle *h, const char *table, ret = -1; goto out; } - if (nft_table_builtin_add(h, table) < 0) { + if (nft_table_builtin_add(h, t) < 0) { /* Built-in table already initialized, skip. */ if (errno == EEXIST) goto out; @@ -394,7 +403,7 @@ __nft_chain_set(struct nft_handle *h, const char *table, _t = nft_table_builtin_find(table); /* if this built-in table does not exists, create it */ if (_t != NULL) - nft_table_builtin_add(h, table); + nft_table_builtin_add(h, _t); _c = nft_chain_builtin_find(_t, chain); if (_c != NULL) { -- cgit v1.2.3 From 3aea037f359795edeb69426e2dde63c59540ba5f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 3 Nov 2012 12:16:54 +0100 Subject: iptables: nft: remove __nft_check_rule Rework code to remove __nft_check_rule and split it into logical fragments. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 142 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 104 insertions(+), 38 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 0d07aa51..123a479c 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2215,24 +2215,33 @@ __nft_rule_del(struct nft_handle *h, struct nft_rule *r) perror("mnl_talk:nft_rule_del"); } -static int -__nft_rule_check(struct nft_handle *h, const char *chain, const char *table, - struct iptables_command_state *cs, - bool delete, bool replace, int rulenum, bool verbose) +static struct nft_rule_list *nft_rule_list_create(struct nft_handle *h) { struct nft_rule_list *list; - struct nft_rule_list_iter *iter; - struct nft_rule *r; - int ret = 0; - int rule_ctr = 0; - bool found = false; list = nft_rule_list_get(h); if (list == NULL) { DEBUGP("cannot retrieve rule list from kernel\n"); - return 0; + return NULL; } + return list; +} + +static void nft_rule_list_destroy(struct nft_rule_list *list) +{ + nft_rule_list_free(list); +} + +static struct nft_rule * +nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, + struct iptables_command_state *cs, int rulenum) +{ + struct nft_rule *r; + struct nft_rule_list_iter *iter; + int rule_ctr = 0; + bool found = false; + iter = nft_rule_list_iter_create(list); if (iter == NULL) { DEBUGP("cannot allocate rule list iterator\n"); @@ -2294,67 +2303,124 @@ next: r = nft_rule_list_iter_next(iter); } - if (found) { - ret = 1; - - if (delete) { - DEBUGP("deleting rule\n"); - __nft_rule_del(h, r); - } else if (replace) - ret = nft_rule_attr_get_u16(r, NFT_RULE_ATTR_HANDLE); - } - nft_rule_list_iter_destroy(iter); - nft_rule_list_free(list); - if (ret == 0) - errno = ENOENT; - - return ret; + return found ? r : NULL; } int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *e, bool verbose) { + struct nft_rule_list *list; + int ret; + nft_fn = nft_rule_check; - return __nft_rule_check(h, chain, table, e, false, false, -1, verbose); + list = nft_rule_list_create(h); + if (list == NULL) { + DEBUGP("cannot allocate rule list\n"); + return 0; + } + + ret = nft_rule_find(list, chain, table, e, -1) ? 1 : 0; + if (ret == 0) + errno = ENOENT; + + nft_rule_list_destroy(list); + + return ret; } int nft_rule_delete(struct nft_handle *h, const char *chain, - const char *table, struct iptables_command_state *e, + const char *table, struct iptables_command_state *cs, bool verbose) { + int ret = 0; + struct nft_rule *r; + struct nft_rule_list *list; + nft_fn = nft_rule_delete; - return __nft_rule_check(h, chain, table, e, true, false, -1, verbose); + list = nft_rule_list_create(h); + if (list == NULL) { + DEBUGP("cannot allocate rule list\n"); + return 0; + } + + r = nft_rule_find(list, chain, table, cs, -1); + if (r != NULL) { + ret = 1; + + DEBUGP("deleting rule\n"); + __nft_rule_del(h, r); + } else + errno = ENOENT; + + nft_rule_list_destroy(list); + + return ret; } int nft_rule_delete_num(struct nft_handle *h, const char *chain, - const char *table, int rulenum, - bool verbose) + const char *table, int rulenum, bool verbose) { + int ret = 0; + struct nft_rule *r; + struct nft_rule_list *list; + nft_fn = nft_rule_delete_num; - return __nft_rule_check(h, chain, table, - NULL, true, false, rulenum, verbose); + list = nft_rule_list_create(h); + if (list == NULL) { + DEBUGP("cannot allocate rule list\n"); + return 0; + } + + r = nft_rule_find(list, chain, table, NULL, rulenum); + if (r != NULL) { + ret = 1; + + DEBUGP("deleting rule by number %d\n", rulenum); + __nft_rule_del(h, r); + } else + errno = ENOENT; + + nft_rule_list_destroy(list); + + return ret; } int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs, int rulenum, bool verbose) { - int handle; + int ret = 0; + struct nft_rule *r; + struct nft_rule_list *list; nft_fn = nft_rule_replace; - handle = __nft_rule_check(h, chain, table, - NULL, false, true, rulenum, verbose); - if (handle < 0) - return handle; + list = nft_rule_list_create(h); + if (list == NULL) { + DEBUGP("cannot allocate rule list\n"); + return 0; + } + + r = nft_rule_find(list, chain, table, cs, rulenum); + if (r != NULL) { + DEBUGP("replacing rule with handle=%u\n", + nft_rule_attr_get_u16(r, NFT_RULE_ATTR_HANDLE)); - return nft_rule_add(h, chain, table, cs, true, handle, verbose); + ret = nft_rule_add(h, chain, table, cs, true, + nft_rule_attr_get_u16(r, NFT_RULE_ATTR_HANDLE), + verbose); + } else + errno = ENOENT; + + nft_rule_list_destroy(list); + + return ret; } /* -- cgit v1.2.3 From 1298a1014bc14c45de50cc242779dfa382c456c9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 3 Nov 2012 12:20:07 +0100 Subject: iptables: nft: use 64-bits handle Now that we use that in kernel space and in libnftables. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 9 +++++---- iptables/nft.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 123a479c..8e2b5acd 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -649,7 +649,7 @@ static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs, - bool append, uint16_t handle, bool verbose) + bool append, uint64_t handle, bool verbose) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -2409,11 +2409,12 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, r = nft_rule_find(list, chain, table, cs, rulenum); if (r != NULL) { - DEBUGP("replacing rule with handle=%u\n", - nft_rule_attr_get_u16(r, NFT_RULE_ATTR_HANDLE)); + DEBUGP("replacing rule with handle=%llu\n", + (unsigned long long) + nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE)); ret = nft_rule_add(h, chain, table, cs, true, - nft_rule_attr_get_u16(r, NFT_RULE_ATTR_HANDLE), + nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE), verbose); } else errno = ENOENT; diff --git a/iptables/nft.h b/iptables/nft.h index 474e652e..aa458f8c 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -39,7 +39,7 @@ int nft_chain_user_rename(struct nft_handle *h, const char *chain, const char *t */ struct nft_rule; -int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool append, uint16_t handle, bool verbose); +int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool append, uint64_t handle, bool verbose); int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose); -- cgit v1.2.3 From 890fd9ef76ad0c11695fb0d09a88169e6e46584f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 3 Nov 2012 13:43:22 +0100 Subject: iptables: nft: use chain types We use the new special chain types defined in the kernel. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 8e2b5acd..24301200 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -86,10 +86,12 @@ static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, #define MANGLE 1 #define RAW 2 #define SECURITY 3 -#define TABLES_MAX 4 +#define NAT 4 +#define TABLES_MAX 5 struct builtin_chain { const char *name; + const char *type; uint32_t prio; uint32_t hook; }; @@ -103,11 +105,13 @@ static struct builtin_table { .chains = { { .name = "PREROUTING", + .type = "filter", .prio = -300, /* NF_IP_PRI_RAW */ .hook = NF_INET_PRE_ROUTING, }, { .name = "OUTPUT", + .type = "filter", .prio = -300, /* NF_IP_PRI_RAW */ .hook = NF_INET_LOCAL_OUT, }, @@ -118,26 +122,31 @@ static struct builtin_table { .chains = { { .name = "PREROUTING", + .type = "filter", .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_PRE_ROUTING, }, { .name = "INPUT", + .type = "filter", .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_LOCAL_IN, }, { .name = "FORWARD", + .type = "filter", .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_FORWARD, }, { .name = "OUTPUT", + .type = "route", .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_LOCAL_OUT, }, { .name = "POSTROUTING", + .type = "filter", .prio = -150, /* NF_IP_PRI_MANGLE */ .hook = NF_INET_POST_ROUTING, }, @@ -148,16 +157,19 @@ static struct builtin_table { .chains = { { .name = "INPUT", + .type = "filter", .prio = 0, /* NF_IP_PRI_FILTER */ .hook = NF_INET_LOCAL_IN, }, { .name = "FORWARD", + .type = "filter", .prio = 0, /* NF_IP_PRI_FILTER */ .hook = NF_INET_FORWARD, }, { .name = "OUTPUT", + .type = "filter", .prio = 0, /* NF_IP_PRI_FILTER */ .hook = NF_INET_LOCAL_OUT, }, @@ -168,22 +180,53 @@ static struct builtin_table { .chains = { { .name = "INPUT", + .type = "filter", .prio = 150, /* NF_IP_PRI_SECURITY */ .hook = NF_INET_LOCAL_IN, }, { .name = "FORWARD", + .type = "filter", .prio = 150, /* NF_IP_PRI_SECURITY */ .hook = NF_INET_FORWARD, }, { .name = "OUTPUT", + .type = "filter", .prio = 150, /* NF_IP_PRI_SECURITY */ .hook = NF_INET_LOCAL_OUT, }, }, }, - /* nat already registered by nf_tables */ + [NAT] = { + .name = "nat", + .chains = { + { + .name = "OUTPUT", + .type = "nat", + .prio = -100, /* NF_IP_PRI_NAT_DST */ + .hook = NF_INET_LOCAL_OUT, + }, + { + .name = "INPUT", + .type = "nat", + .prio = 100, /* NF_IP_PRI_NAT_SRC */ + .hook = NF_INET_LOCAL_IN, + }, + { + .name = "PREROUTING", + .type = "nat", + .prio = -100, /* NF_IP_PRI_NAT_DST */ + .hook = NF_INET_PRE_ROUTING, + }, + { + .name = "POSTROUTING", + .type = "nat", + .prio = 100, /* NF_IP_PRI_NAT_SRC */ + .hook = NF_INET_POST_ROUTING, + }, + }, + }, }; static int nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t) @@ -227,6 +270,7 @@ nft_chain_builtin_alloc(struct builtin_table *table, nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_HOOKNUM, chain->hook); nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_PRIO, chain->prio); nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy); + nft_chain_attr_set(c, NFT_CHAIN_ATTR_TYPE, (char *)chain->type); return c; } @@ -243,8 +287,10 @@ nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, if (c == NULL) return; + /* NLM_F_CREATE requests module autoloading */ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, - NLM_F_ACK|NLM_F_EXCL, h->seq); + NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, + h->seq); nft_chain_nlmsg_build_payload(nlh, c); nft_chain_free(c); -- cgit v1.2.3 From 5705ea1f4e3c9cd3d5d9cbcf84b9733ce1f07e57 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 19 Nov 2012 15:32:18 +0100 Subject: xtables-restore: add support for dormant tables This patch adds support for dormant tables for xtables-restore. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 10 +++++++ iptables/nft.c | 55 +++++++++++++++++++++++++++++++++++-- iptables/nft.h | 2 ++ iptables/xtables-restore.c | 8 +++++- 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 542b6549..837bab32 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -53,9 +53,19 @@ enum nft_hook_attributes { }; #define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1) +/** + * enum nft_table_flags - nf_tables table flags + * + * @NFT_TABLE_F_DORMANT: this table is not active + */ +enum nft_table_flags { + NFT_TABLE_F_DORMANT = 0x1, +}; + enum nft_table_attributes { NFTA_TABLE_UNSPEC, NFTA_TABLE_NAME, + NFTA_TABLE_FLAGS, __NFTA_TABLE_MAX }; #define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1) diff --git a/iptables/nft.c b/iptables/nft.c index 24301200..1f5ecb70 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -229,7 +229,9 @@ static struct builtin_table { }, }; -static int nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t) +static int +nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, + bool dormant) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -241,6 +243,10 @@ static int nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t) return -1; nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)_t->name); + if (dormant) { + nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS, + NFT_TABLE_F_DORMANT); + } nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, NLM_F_ACK|NLM_F_EXCL, h->seq); @@ -367,7 +373,7 @@ nft_chain_builtin_init(struct nft_handle *h, const char *table, ret = -1; goto out; } - if (nft_table_builtin_add(h, t) < 0) { + if (nft_table_builtin_add(h, t, false) < 0) { /* Built-in table already initialized, skip. */ if (errno == EEXIST) goto out; @@ -423,6 +429,49 @@ int nft_chain_add(struct nft_handle *h, const struct nft_chain *c) return mnl_talk(h, nlh, NULL, NULL); } +int nft_table_set_dormant(struct nft_handle *h, const char *table) +{ + int ret = 0, i; + struct builtin_table *t; + + t = nft_table_builtin_find(table); + if (t == NULL) { + ret = -1; + goto out; + } + /* Add this table as dormant */ + if (nft_table_builtin_add(h, t, true) < 0) { + /* Built-in table already initialized, skip. */ + if (errno == EEXIST) + goto out; + } + for (i=0; t->chains[i].name != NULL && ichains[i].name, NF_ACCEPT); +out: + return ret; +} + +int nft_table_wake_dormant(struct nft_handle *h, const char *table) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_table *t; + + t = nft_table_alloc(); + if (t == NULL) + return -1; + + nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table); + nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS, 0); + + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + NLM_F_ACK, h->seq); + nft_table_nlmsg_build_payload(nlh, t); + nft_table_free(t); + + return mnl_talk(h, nlh, NULL, NULL); +} + static void nft_chain_print_debug(struct nft_chain *c, struct nlmsghdr *nlh) { #ifdef NLDEBUG @@ -449,7 +498,7 @@ __nft_chain_set(struct nft_handle *h, const char *table, _t = nft_table_builtin_find(table); /* if this built-in table does not exists, create it */ if (_t != NULL) - nft_table_builtin_add(h, _t); + nft_table_builtin_add(h, _t, false); _c = nft_chain_builtin_find(_t, chain); if (_c != NULL) { diff --git a/iptables/nft.h b/iptables/nft.h index aa458f8c..aed2498b 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -20,6 +20,8 @@ struct nft_table; int nft_table_add(struct nft_handle *h, const struct nft_table *t); int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, bool counters), bool counters); bool nft_table_find(struct nft_handle *h, const char *tablename); +int nft_table_set_dormant(struct nft_handle *h, const char *table); +int nft_table_wake_dormant(struct nft_handle *h, const char *table); /* * Operations with chains. diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 09922a0c..30ea813c 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -243,11 +243,16 @@ xtables_restore_main(int argc, char *argv[]) fputs(buffer, stdout); continue; } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) { - /* FIXME commit/testing operation not supported */ if (!testing) { + if (nft_table_wake_dormant(&h, curtable) < 0) { + fprintf(stderr, "Failed to wake up " + "dormant table `%s'\n", + curtable); + } DEBUGP("Calling commit\n"); ret = 1; } else { + /* FIXME -t needs to be fixed */ DEBUGP("Not calling commit, testing\n"); ret = 1; } @@ -270,6 +275,7 @@ xtables_restore_main(int argc, char *argv[]) if (tablename && (strcmp(tablename, table) != 0)) continue; + nft_table_set_dormant(&h, table); if (noflush == 0) { DEBUGP("Cleaning all chains of table '%s'\n", table); -- cgit v1.2.3 From 9c541721d318598db45986ee2fd61491fadb53d0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 29 Dec 2012 20:05:55 +0100 Subject: nft: adapt chain rename to recent Patrick's updates This patch gets existing code in sync with Patrick's chain renaming new approach. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 3 +- iptables/nft.c | 55 +++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 837bab32..bdab3f2e 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -73,11 +73,12 @@ enum nft_table_attributes { enum nft_chain_attributes { NFTA_CHAIN_UNSPEC, NFTA_CHAIN_TABLE, + NFTA_CHAIN_HANDLE, NFTA_CHAIN_NAME, NFTA_CHAIN_HOOK, NFTA_CHAIN_POLICY, NFTA_CHAIN_USE, - NFTA_CHAIN_NEW_NAME, + NFTA_CHAIN_TYPE, __NFTA_CHAIN_MAX }; #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) diff --git a/iptables/nft.c b/iptables/nft.c index 1f5ecb70..46c8c948 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1686,17 +1686,66 @@ err: return ret == 0 ? 1 : 0; } +static struct nft_chain * +nft_chain_find(struct nft_handle *h, const char *table, const char *chain) +{ + struct nft_chain_list *list; + struct nft_chain_list_iter *iter; + struct nft_chain *c; + + list = nft_chain_list_get(h); + if (list == NULL) { + DEBUGP("cannot allocate chain list\n"); + return NULL; + } + + iter = nft_chain_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return NULL; + } + + c = nft_chain_list_iter_next(iter); + while (c != NULL) { + const char *table_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE); + const char *chain_name = + nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); + + if (strcmp(table, table_name) != 0) + goto next; + + if (strcmp(chain, chain_name) != 0) + goto next; + + return c; +next: + c = nft_chain_list_iter_next(iter); + } + return NULL; +} + int nft_chain_user_rename(struct nft_handle *h,const char *chain, const char *table, const char *newname) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; struct nft_chain *c; + uint64_t handle; int ret; /* If built-in chains don't exist for this table, create them */ nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + /* Find the old chain to be renamed */ + c = nft_chain_find(h, table, chain); + if (c == NULL) { + errno = ENOENT; + return -1; + } + handle = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_HANDLE); + + /* Now prepare the new name for the chain */ c = nft_chain_alloc(); if (c == NULL) { DEBUGP("cannot allocate chain\n"); @@ -1704,11 +1753,11 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, } nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); - nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); - nft_chain_attr_set(c, NFT_CHAIN_ATTR_NEW_NAME, (char *)newname); + nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)newname); + nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_HANDLE, handle); nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, - NLM_F_ACK|NLM_F_REPLACE, h->seq); + NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); nft_chain_free(c); -- cgit v1.2.3 From 5a1b519d1e26767fa1f0de15b0f7e125531a1719 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 30 Dec 2012 01:45:44 +0100 Subject: xtables: fix crash due to using wrong globals Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index d1a91723..d6045906 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -623,11 +623,11 @@ static void command_jump(struct iptables_command_state *cs) xs_init_target(cs->target); if (cs->target->x6_options != NULL) - opts = xtables_options_xfrm(iptables_globals.orig_opts, opts, + opts = xtables_options_xfrm(xtables_globals.orig_opts, opts, cs->target->x6_options, &cs->target->option_offset); else - opts = xtables_merge_options(iptables_globals.orig_opts, opts, + opts = xtables_merge_options(xtables_globals.orig_opts, opts, cs->target->extra_opts, &cs->target->option_offset); if (opts == NULL) @@ -661,10 +661,10 @@ static void command_match(struct iptables_command_state *cs) return; /* Merge options for non-cloned matches */ if (m->x6_options != NULL) - opts = xtables_options_xfrm(iptables_globals.orig_opts, opts, + opts = xtables_options_xfrm(xtables_globals.orig_opts, opts, m->x6_options, &m->option_offset); else if (m->extra_opts != NULL) - opts = xtables_merge_options(iptables_globals.orig_opts, opts, + opts = xtables_merge_options(xtables_globals.orig_opts, opts, m->extra_opts, &m->option_offset); if (opts == NULL) xtables_error(OTHER_PROBLEM, "can't alloc memory!"); @@ -1048,7 +1048,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) exit_tryhelp(2); default: - if (command_default(&cs, &iptables_globals) == 1) + if (command_default(&cs, &xtables_globals) == 1) /* cf. ip6tables.c */ continue; break; -- cgit v1.2.3 From 0391677c1a0b28c14d01febd9628a543e8e5fd62 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Sun, 13 Jan 2013 16:42:11 +0100 Subject: xtables: add IPv6 support Summary of changes to add IPv6 support to the xtables utility: * modify all commands (add, delete, replace, check and listing) to support IPv6 addresses. And for the internal nft library: * add family to struct nft_handle and modify all caller to use this family instead of the hardcoded AF_INET. * move code that we can re-use for IPv4 and IPv6 into helper functions. * add IPv6 rule printing support. * add support to parse IPv6 address. Pablo added several improvements to this patch: * added basic xtables-save and xtables-restore support (so it defaults to IPv4) * fixed a couple of bugs found while testing * added reference when -f is used to point to -m frag (until we can make this consistent with IPv4). Note that we use one single xtables binary utility for IPv4 and IPv6. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 655 +++++++++++++++++++++++++++++++++------------ iptables/nft.h | 1 + iptables/xtables-restore.c | 4 +- iptables/xtables-save.c | 4 +- iptables/xtables.c | 400 +++++++++++++++++++-------- 5 files changed, 772 insertions(+), 292 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 46c8c948..43b13deb 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -34,7 +34,9 @@ #include #include -#include /* FIXME: only IPV4 by now */ +#include +#include +#include #include #include @@ -248,7 +250,7 @@ nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, NFT_TABLE_F_DORMANT); } - nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_table_nlmsg_build_payload(nlh, t); nft_table_free(t); @@ -294,7 +296,7 @@ nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, return; /* NLM_F_CREATE requests module autoloading */ - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -410,7 +412,7 @@ int nft_table_add(struct nft_handle *h, const struct nft_table *t) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; - nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_table_nlmsg_build_payload(nlh, t); @@ -422,7 +424,7 @@ int nft_chain_add(struct nft_handle *h, const struct nft_chain *c) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -464,7 +466,7 @@ int nft_table_wake_dormant(struct nft_handle *h, const char *table) nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table); nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS, 0); - nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family, NLM_F_ACK, h->seq); nft_table_nlmsg_build_payload(nlh, t); nft_table_free(t); @@ -525,7 +527,7 @@ __nft_chain_set(struct nft_handle *h, const char *table, counters->pcnt); } - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -741,6 +743,78 @@ static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) nft_rule_add_expr(r, expr); } +static void add_iniface(struct nft_rule *r, char *iface, int invflags) +{ + int iface_len; + uint32_t op; + + iface_len = strlen(iface); + + if (invflags & IPT_INV_VIA_IN) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + if (iface[iface_len - 1] == '+') { + add_meta(r, NFT_META_IIFNAME); + add_cmp_ptr(r, op, iface, iface_len - 1); + } else { + add_meta(r, NFT_META_IIF); + add_cmp_u32(r, if_nametoindex(iface), op); + } +} + +static void add_outiface(struct nft_rule *r, char *iface, int invflags) +{ + int iface_len; + uint32_t op; + + iface_len = strlen(iface); + + if (invflags & IPT_INV_VIA_OUT) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + if (iface[iface_len - 1] == '+') { + add_meta(r, NFT_META_OIFNAME); + add_cmp_ptr(r, op, iface, iface_len - 1); + } else { + add_meta(r, NFT_META_OIF); + add_cmp_u32(r, if_nametoindex(iface), op); + } +} + +static void add_addr(struct nft_rule *r, int offset, + void *data, size_t len, int invflags) +{ + uint32_t op; + + add_payload(r, offset, len); + + if (invflags & IPT_INV_SRCIP || invflags & IPT_INV_DSTIP) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_ptr(r, op, data, len); +} + +static void add_proto(struct nft_rule *r, int offset, size_t len, + uint32_t proto, int invflags) +{ + uint32_t op; + + add_payload(r, offset, len); + + if (invflags & XT_INV_PROTO) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_u32(r, proto, op); +} + int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs, @@ -753,6 +827,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, int ret = 1; uint32_t op; int flags = append ? NLM_F_APPEND : 0; + int ip_flags = 0; /* If built-in chains don't exist for this table, create them */ nft_chain_builtin_init(h, table, chain, NF_ACCEPT); @@ -768,77 +843,72 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); - if (cs->fw.ip.iniface[0] != '\0') { - int iface_len = strlen(cs->fw.ip.iniface); - - if (cs->fw.ip.invflags & IPT_INV_VIA_IN) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; - - if (cs->fw.ip.iniface[iface_len - 1] == '+') { - add_meta(r, NFT_META_IIFNAME); - add_cmp_ptr(r, op, cs->fw.ip.iniface, iface_len - 1); - } else { - add_meta(r, NFT_META_IIF); - add_cmp_u32(r, if_nametoindex(cs->fw.ip.iniface), op); + switch (h->family) { + case AF_INET: + if (cs->fw.ip.iniface[0] != '\0') + add_iniface(r, cs->fw.ip.iniface, cs->fw.ip.invflags); + + if (cs->fw.ip.outiface[0] != '\0') + add_outiface(r, cs->fw.ip.outiface, + cs->fw.ip.invflags); + + if (cs->fw.ip.src.s_addr != 0) + add_addr(r, offsetof(struct iphdr, saddr), + &cs->fw.ip.src.s_addr, 4, + cs->fw.ip.invflags); + + if (cs->fw.ip.dst.s_addr != 0) + add_addr(r, offsetof(struct iphdr, daddr), + &cs->fw.ip.dst.s_addr, 4, + cs->fw.ip.invflags); + + if (cs->fw.ip.proto != 0) + add_proto(r, offsetof(struct iphdr, protocol), 1, + cs->fw.ip.proto, cs->fw.ip.invflags); + + if (cs->fw.ip.flags & IPT_F_FRAG) { + add_payload(r, offsetof(struct iphdr, frag_off), 2); + /* get the 13 bits that contain the fragment offset */ + add_bitwise_u16(r, 0x1fff, !0x1fff); + + /* if offset is non-zero, this is a fragment */ + if (cs->fw.ip.invflags & IPT_INV_FRAG) + op = NFT_CMP_EQ; + else + op = NFT_CMP_NEQ; + + add_cmp_u16(r, 0, op); } - } - if (cs->fw.ip.outiface[0] != '\0') { - int iface_len = strlen(cs->fw.ip.outiface); - if (cs->fw.ip.invflags & IPT_INV_VIA_OUT) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; + ip_flags = cs->fw.ip.flags; - if (cs->fw.ip.outiface[iface_len - 1] == '+') { - add_meta(r, NFT_META_OIFNAME); - add_cmp_ptr(r, op, cs->fw.ip.outiface, iface_len - 1); - } else { - add_meta(r, NFT_META_OIF); - add_cmp_u32(r, if_nametoindex(cs->fw.ip.outiface), op); - } - } - if (cs->fw.ip.src.s_addr != 0) { - add_payload(r, offsetof(struct iphdr, saddr), 4); - if (cs->fw.ip.invflags & IPT_INV_SRCIP) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; + break; + case AF_INET6: + if (cs->fw6.ipv6.iniface[0] != '\0') + add_iniface(r, cs->fw6.ipv6.iniface, + cs->fw6.ipv6.invflags); - add_cmp_u32(r, cs->fw.ip.src.s_addr, op); - } - if (cs->fw.ip.dst.s_addr != 0) { - add_payload(r, offsetof(struct iphdr, daddr), 4); - if (cs->fw.ip.invflags & IPT_INV_DSTIP) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; + if (cs->fw6.ipv6.outiface[0] != '\0') + add_outiface(r, cs->fw6.ipv6.outiface, + cs->fw6.ipv6.invflags); - add_cmp_u32(r, cs->fw.ip.dst.s_addr, op); - } - if (cs->fw.ip.proto != 0) { - add_payload(r, offsetof(struct iphdr, protocol), 1); - if (cs->fw.ip.invflags & XT_INV_PROTO) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; + if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)) + add_addr(r, offsetof(struct ip6_hdr, ip6_src), + &cs->fw6.ipv6.src, 16, + cs->fw6.ipv6.invflags); - add_cmp_u32(r, cs->fw.ip.proto, op); - } - if (cs->fw.ip.flags & IPT_F_FRAG) { - add_payload(r, offsetof(struct iphdr, frag_off), 2); - /* get the 13 bits that contain the fragment offset */ - add_bitwise_u16(r, 0x1fff, !0x1fff); + if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)) + add_addr(r, offsetof(struct ip6_hdr, ip6_dst), + &cs->fw6.ipv6.dst, 16, + cs->fw6.ipv6.invflags); - /* if offset is non-zero, this is a fragment */ - if (cs->fw.ip.invflags & IPT_INV_FRAG) - op = NFT_CMP_EQ; - else - op = NFT_CMP_NEQ; + if (cs->fw6.ipv6.proto != 0) + add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1, + cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags); + + ip_flags = cs->fw6.ipv6.flags; - add_cmp_u16(r, 0, op); + break; } for (matchp = cs->matches; matchp; matchp = matchp->next) @@ -862,7 +932,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, add_target(r, cs->target->t); } else if (strlen(cs->jumpto) > 0) { /* Not standard, then it's a go / jump to chain */ - if (cs->fw.ip.flags & IPT_F_GOTO) + if (ip_flags & IPT_F_GOTO) add_jumpto(r, cs->jumpto, NFT_GOTO); else add_jumpto(r, cs->jumpto, NFT_JUMP); @@ -877,7 +947,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, } nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, - AF_INET, flags, h->seq); + h->family, flags, h->seq); nft_rule_nlmsg_build_payload(nlh, r); @@ -1205,7 +1275,7 @@ static const char *mask_to_str(uint32_t mask) } static void -nft_print_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) +nft_print_payload_ipv4(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) { uint32_t offset; bool inv; @@ -1248,6 +1318,48 @@ nft_print_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) } } +static void +nft_print_payload_ipv6(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) +{ + uint32_t offset; + bool inv; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + switch (offset) { + char addr_str[INET6_ADDRSTRLEN]; + struct in6_addr addr; + uint8_t proto; + case offsetof(struct ip6_hdr, ip6_src): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); + + if (inv) + printf("! -s %s ", addr_str); + else + printf("-s %s ", addr_str); + + break; + case offsetof(struct ip6_hdr, ip6_dst): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); + + if (inv) + printf("! -d %s ", addr_str); + else + printf("-d %s ", addr_str); + + break; + case offsetof(struct ip6_hdr, ip6_nxt): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + print_proto(proto, inv); + break; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + static void nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, bool counters) @@ -1279,7 +1391,10 @@ static void nft_rule_print_save(struct nft_rule *r, bool counters) if (strcmp(name, "counter") == 0) { nft_print_counters(expr, iter, counters); } else if (strcmp(name, "payload") == 0) { - nft_print_payload(expr, iter); + if (nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY) == AF_INET) + nft_print_payload_ipv4(expr, iter); + else + nft_print_payload_ipv6(expr, iter); } else if (strcmp(name, "meta") == 0) { nft_print_meta(expr, iter); } else if (strcmp(name, "match") == 0) { @@ -1334,7 +1449,7 @@ static struct nft_chain_list *nft_chain_list_get(struct nft_handle *h) return 0; } - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family, NLM_F_DUMP, h->seq); ret = mnl_talk(h, nlh, nft_chain_list_cb, list); @@ -1446,7 +1561,7 @@ static struct nft_rule_list *nft_rule_list_get(struct nft_handle *h) return 0; } - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, AF_INET, + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family, NLM_F_DUMP, h->seq); ret = mnl_talk(h, nlh, nft_rule_list_cb, list); @@ -1512,7 +1627,7 @@ __nft_rule_flush(struct nft_handle *h, const char *table, const char *chain) nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); /* Delete all rules in this table + chain */ - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family, NLM_F_ACK, h->seq); nft_rule_nlmsg_build_payload(nlh, r); nft_rule_free(r); @@ -1589,7 +1704,7 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_chain_nlmsg_build_payload(nlh, c); nft_chain_free(c); @@ -1610,7 +1725,7 @@ static int __nft_chain_del(struct nft_handle *h, struct nft_chain *c) struct nlmsghdr *nlh; int ret; - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, h->family, NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -1756,7 +1871,7 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)newname); nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_HANDLE, handle); - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); nft_chain_free(c); @@ -1809,7 +1924,7 @@ static struct nft_table_list *nft_table_list_get(struct nft_handle *h) return 0; } - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, AF_INET, + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family, NLM_F_DUMP, h->seq); ret = mnl_talk(h, nlh, nft_table_list_cb, list); @@ -1919,39 +2034,86 @@ match_different(const struct xt_entry_match *a, } static bool -is_same(const struct iptables_command_state *a, const struct iptables_command_state *b) +is_same(int family, const struct iptables_command_state *a, + const struct iptables_command_state *b) { unsigned int i; + const char *a_outiface, *a_iniface; + unsigned const char *a_iniface_mask, *a_outiface_mask; + const char *b_outiface, *b_iniface; + unsigned const char *b_iniface_mask, *b_outiface_mask; /* Always compare head structures: ignore mask here. */ - if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr - || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr - || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr - || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr - || a->fw.ip.proto != b->fw.ip.proto - || a->fw.ip.flags != b->fw.ip.flags - || a->fw.ip.invflags != b->fw.ip.invflags) { - DEBUGP("different src/dst/proto/flags/invflags\n"); + switch (family) { + case AF_INET: + if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr + || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr + || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr + || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr + || a->fw.ip.proto != b->fw.ip.proto + || a->fw.ip.flags != b->fw.ip.flags + || a->fw.ip.invflags != b->fw.ip.invflags) { + DEBUGP("different src/dst/proto/flags/invflags\n"); + return false; + } + + a_iniface_mask = a->fw.ip.iniface_mask; + a_iniface = a->fw.ip.iniface; + a_outiface_mask = a->fw.ip.outiface_mask; + a_outiface = a->fw.ip.outiface; + + b_iniface_mask = b->fw.ip.iniface_mask; + b_iniface = b->fw.ip.iniface; + b_outiface_mask = b->fw.ip.outiface_mask; + b_outiface = b->fw.ip.outiface; + + break; + case AF_INET6: + if (memcmp(a->fw6.ipv6.src.s6_addr, + b->fw6.ipv6.src.s6_addr, + sizeof(struct in6_addr)) != 0 || + memcmp(a->fw6.ipv6.dst.s6_addr, + b->fw6.ipv6.dst.s6_addr, + sizeof(struct in6_addr)) != 0 || + a->fw6.ipv6.proto != b->fw6.ipv6.proto || + a->fw6.ipv6.flags != b->fw6.ipv6.flags || + a->fw6.ipv6.invflags != b->fw6.ipv6.invflags) { + DEBUGP("different src/dst/proto/flags/invflags\n"); + return false; + } + + a_iniface_mask = a->fw6.ipv6.iniface_mask; + a_iniface = a->fw6.ipv6.iniface; + a_outiface_mask = a->fw6.ipv6.outiface_mask; + a_outiface = a->fw6.ipv6.outiface; + + b_iniface_mask = b->fw6.ipv6.iniface_mask; + b_iniface = b->fw6.ipv6.iniface; + b_outiface_mask = b->fw6.ipv6.outiface_mask; + b_outiface = b->fw6.ipv6.outiface; + + break; + default: return false; } for (i = 0; i < IFNAMSIZ; i++) { - if (a->fw.ip.iniface_mask[i] != b->fw.ip.iniface_mask[i]) { + if (a_iniface_mask[i] != b_iniface_mask[i]) { DEBUGP("different iniface mask %x, %x (%d)\n", - a->fw.ip.iniface_mask[i] & 0xff, b->fw.ip.iniface_mask[i] & 0xff, i); + a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i); return false; } - if ((a->fw.ip.iniface[i] & a->fw.ip.iniface_mask[i]) - != (b->fw.ip.iniface[i] & b->fw.ip.iniface_mask[i])) { + if ((a_iniface[i] & a_iniface_mask[i]) + != (b_iniface[i] & b_iniface_mask[i])) { DEBUGP("different iniface\n"); return false; } - if (a->fw.ip.outiface_mask[i] != b->fw.ip.outiface_mask[i]) { + if (a_outiface_mask[i] != b_outiface_mask[i]) { DEBUGP("different outiface mask\n"); return false; } - if ((a->fw.ip.outiface[i] & a->fw.ip.outiface_mask[i]) - != (b->fw.ip.outiface[i] & b->fw.ip.outiface_mask[i])) { + if ((a_outiface[i] & a_outiface_mask[i]) + != (b_outiface[i] & b_outiface_mask[i])) { DEBUGP("different outiface\n"); return false; } @@ -1962,13 +2124,16 @@ is_same(const struct iptables_command_state *a, const struct iptables_command_st static void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) + int family, struct iptables_command_state *cs) { uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); uint32_t value; const char *name; const void *ifname; + char *iniface, *outiface; + unsigned char *iniface_mask, *outiface_mask; size_t len; + uint8_t *invflags; e = nft_rule_expr_iter_next(iter); if (e == NULL) @@ -1980,58 +2145,75 @@ nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, return; } + switch (family) { + case AF_INET: + iniface = cs->fw.ip.iniface; + outiface = cs->fw.ip.outiface; + iniface_mask = cs->fw.ip.iniface_mask; + outiface_mask = cs->fw.ip.outiface_mask; + invflags = &cs->fw.ip.invflags; + break; + case AF_INET6: + iniface = cs->fw6.ipv6.iniface; + outiface = cs->fw6.ipv6.outiface; + iniface_mask = cs->fw6.ipv6.iniface_mask; + outiface_mask = cs->fw6.ipv6.outiface_mask; + invflags = &cs->fw6.ipv6.invflags; + break; + default: + return; + } + switch(key) { case NFT_META_IIF: value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= IPT_INV_VIA_IN; + *invflags |= IPT_INV_VIA_IN; - if_indextoname(value, cs->fw.ip.iniface); + if_indextoname(value, iniface); - memset(cs->fw.ip.iniface_mask, 0xff, - strlen(cs->fw.ip.iniface)+1); + memset(iniface_mask, 0xff, strlen(iniface)+1); break; case NFT_META_OIF: value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= IPT_INV_VIA_OUT; + *invflags |= IPT_INV_VIA_OUT; - if_indextoname(value, cs->fw.ip.outiface); + if_indextoname(value, outiface); - memset(cs->fw.ip.outiface_mask, 0xff, - strlen(cs->fw.ip.outiface)+1); + memset(outiface_mask, 0xff, strlen(outiface)+1); break; case NFT_META_IIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= IPT_INV_VIA_IN; + *invflags |= IPT_INV_VIA_IN; - memcpy(cs->fw.ip.iniface, ifname, len); - cs->fw.ip.iniface[len] = '\0'; + memcpy(iniface, ifname, len); + iniface[len] = '\0'; /* If zero, then this is an interface mask */ - if (if_nametoindex(cs->fw.ip.iniface) == 0) { - cs->fw.ip.iniface[len] = '+'; - cs->fw.ip.iniface[len+1] = '\0'; + if (if_nametoindex(iniface) == 0) { + iniface[len] = '+'; + iniface[len+1] = '\0'; } - memset(cs->fw.ip.iniface_mask, 0xff, len); + memset(iniface_mask, 0xff, len); break; case NFT_META_OIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= IPT_INV_VIA_OUT; + *invflags |= IPT_INV_VIA_OUT; - memcpy(cs->fw.ip.outiface, ifname, len); - cs->fw.ip.outiface[len] = '\0'; + memcpy(outiface, ifname, len); + outiface[len] = '\0'; /* If zero, then this is an interface mask */ - if (if_nametoindex(cs->fw.ip.outiface) == 0) { - cs->fw.ip.outiface[len] = '+'; - cs->fw.ip.outiface[len+1] = '\0'; + if (if_nametoindex(outiface) == 0) { + outiface[len] = '+'; + outiface[len+1] = '\0'; } - memset(cs->fw.ip.outiface_mask, 0xff, len); + memset(outiface_mask, 0xff, len); break; default: DEBUGP("unknown meta key %d\n", key); @@ -2040,17 +2222,13 @@ nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, } static void -nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) +nft_parse_payload_ipv4(uint32_t offset, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) { - uint32_t offset; - bool inv; - - offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - switch(offset) { struct in_addr addr; uint8_t proto; + bool inv; case offsetof(struct iphdr, saddr): get_cmp_data(iter, &addr, sizeof(addr), &inv); @@ -2084,6 +2262,56 @@ nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, } } +static void +nft_parse_payload_ipv6(uint32_t offset, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) +{ + switch (offset) { + struct in6_addr addr; + uint8_t proto; + bool inv; + + case offsetof(struct ip6_hdr, ip6_src): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr)); + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_SRCIP; + break; + case offsetof(struct ip6_hdr, ip6_dst): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr)); + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_DSTIP; + break; + case offsetof(struct ip6_hdr, ip6_nxt): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + cs->fw6.ipv6.proto = proto; + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_PROTO; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + +static void +nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + int family, struct iptables_command_state *cs) +{ + uint32_t offset; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + switch (family) { + case AF_INET: + nft_parse_payload_ipv4(offset, iter, cs); + break; + case AF_INET6: + nft_parse_payload_ipv6(offset, iter, cs); + break; + } +} + static void nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, struct xt_counters *counters) @@ -2094,7 +2322,7 @@ nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, static void nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) + int family, struct iptables_command_state *cs) { int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); @@ -2111,7 +2339,10 @@ nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, cs->jumpto = "RETURN"; return; case NFT_GOTO: - cs->fw.ip.flags |= IPT_F_GOTO; + if (family == AF_INET) + cs->fw.ip.flags |= IPT_F_GOTO; + else + cs->fw6.ipv6.flags |= IPT_F_GOTO; case NFT_JUMP: cs->jumpto = chain; return; @@ -2124,6 +2355,7 @@ nft_rule_to_iptables_command_state(struct nft_rule *r, { struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; + int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); iter = nft_rule_expr_iter_create(r); if (iter == NULL) @@ -2137,11 +2369,11 @@ nft_rule_to_iptables_command_state(struct nft_rule *r, if (strcmp(name, "counter") == 0) { nft_parse_counter(expr, iter, &cs->counters); } else if (strcmp(name, "payload") == 0) { - nft_parse_payload(expr, iter, cs); + nft_parse_payload(expr, iter, family, cs); } else if (strcmp(name, "meta") == 0) { - nft_parse_meta(expr, iter, cs); + nft_parse_meta(expr, iter, family, cs); } else if (strcmp(name, "immediate") == 0) { - nft_parse_immediate(expr, iter, cs); + nft_parse_immediate(expr, iter, family, cs); } expr = nft_rule_expr_iter_next(iter); @@ -2348,7 +2580,7 @@ __nft_rule_del(struct nft_handle *h, struct nft_rule *r) struct nlmsghdr *nlh; int ret; - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, AF_INET, + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family, NLM_F_ACK, h->seq); nft_rule_nlmsg_build_payload(nlh, r); @@ -2421,7 +2653,7 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, nft_rule_to_iptables_command_state(r, &this); - if (!is_same(cs, &this)) + if (!is_same(nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY), cs, &this)) goto next; if (!find_matches(cs->matches, r)) { @@ -2683,6 +2915,73 @@ print_match(struct nft_rule_expr *expr, int numeric) free(m); } +static void +print_ipv4_addr(const struct iptables_command_state *cs, unsigned int format) +{ + char buf[BUFSIZ]; + + fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src)); + else + strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src)); + strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); + printf(FMT("%-19s ","%s "), buf); + } + + fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst)); + else + strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst)); + strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); + printf(FMT("%-19s ","-> %s"), buf); + } +} + +static void +print_ipv6_addr(const struct iptables_command_state *cs, unsigned int format) +{ + char buf[BUFSIZ]; + + fputc(cs->fw6.ipv6.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src) + && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, + xtables_ip6addr_to_numeric(&cs->fw6.ipv6.src)); + else + strcpy(buf, + xtables_ip6addr_to_anyname(&cs->fw6.ipv6.src)); + strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.smsk)); + printf(FMT("%-19s ","%s "), buf); + } + + + fputc(cs->fw6.ipv6.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst) + && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, + xtables_ip6addr_to_numeric(&cs->fw6.ipv6.dst)); + else + strcpy(buf, + xtables_ip6addr_to_anyname(&cs->fw6.ipv6.dst)); + strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.dmsk)); + printf(FMT("%-19s ","-> %s"), buf); + } +} + static void print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, unsigned int num, unsigned int format) @@ -2690,8 +2989,11 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, const struct xtables_target *target = NULL; const char *targname = NULL; const void *targinfo = NULL; - uint8_t flags; - char buf[BUFSIZ]; + int family; + uint8_t flags = 0; + uint8_t invflags = 0; + uint8_t proto = 0; + const char *iniface = NULL, *outiface = NULL; struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; struct xt_entry_target *t; @@ -2709,8 +3011,10 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); if (strcmp(name, "target") == 0) { - targname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME); - targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &target_len); + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_TG_NAME); + targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, + &target_len); break; } else if (strcmp(name, "immediate") == 0) { uint32_t verdict = @@ -2727,10 +3031,12 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, targname = "RETURN"; break; case NFT_GOTO: - targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN); + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_IMM_CHAIN); break; case NFT_JUMP: - targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN); + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_IMM_CHAIN); break; } } @@ -2738,7 +3044,24 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, } nft_rule_expr_iter_destroy(iter); - flags = cs->fw.ip.flags; + family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); + + switch (family) { + case AF_INET: + flags = cs->fw.ip.flags; + invflags = flags = cs->fw.ip.invflags; + proto = cs->fw.ip.proto; + iniface = cs->fw.ip.iniface; + outiface = cs->fw.ip.outiface; + break; + case AF_INET6: + flags = cs->fw6.ipv6.flags; + invflags = cs->fw6.ipv6.invflags; + proto = cs->fw6.ipv6.proto; + iniface = cs->fw6.ipv6.iniface; + outiface = cs->fw6.ipv6.outiface; + break; + } if (format & FMT_LINENUMBERS) printf(FMT("%-4u ", "%u "), num); @@ -2751,82 +3074,68 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, if (!(format & FMT_NOTARGET)) printf(FMT("%-9s ", "%s "), targname ? targname : ""); - fputc(cs->fw.ip.invflags & XT_INV_PROTO ? '!' : ' ', stdout); + fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout); { const char *pname = - proto_to_name(cs->fw.ip.proto, format&FMT_NUMERIC); + proto_to_name(proto, format&FMT_NUMERIC); if (pname) printf(FMT("%-5s", "%s "), pname); else - printf(FMT("%-5hu", "%hu "), cs->fw.ip.proto); + printf(FMT("%-5hu", "%hu "), proto); } if (format & FMT_OPTIONS) { if (format & FMT_NOTABLE) fputs("opt ", stdout); - fputc(cs->fw.ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); fputc(' ', stdout); } if (format & FMT_VIA) { char iface[IFNAMSIZ+2]; - if (cs->fw.ip.invflags & IPT_INV_VIA_IN) { + if (invflags & IPT_INV_VIA_IN) { iface[0] = '!'; iface[1] = '\0'; } else iface[0] = '\0'; - if (cs->fw.ip.iniface[0] != '\0') { - strcat(iface, cs->fw.ip.iniface); + if (iniface[0] != '\0') { + strcat(iface, iniface); } else if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); printf(FMT(" %-6s ","in %s "), iface); - if (cs->fw.ip.invflags & IPT_INV_VIA_OUT) { + if (invflags & IPT_INV_VIA_OUT) { iface[0] = '!'; iface[1] = '\0'; } else iface[0] = '\0'; - if (cs->fw.ip.outiface[0] != '\0') { - strcat(iface, cs->fw.ip.outiface); + if (outiface[0] != '\0') { + strcat(iface, outiface); } else if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); printf(FMT("%-6s ","out %s "), iface); } - fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); - if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) - printf(FMT("%-19s ","%s "), "anywhere"); - else { - if (format & FMT_NUMERIC) - strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src)); - else - strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src)); - strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); - printf(FMT("%-19s ","%s "), buf); - } - fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); - if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) - printf(FMT("%-19s ","-> %s"), "anywhere"); - else { - if (format & FMT_NUMERIC) - strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst)); - else - strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst)); - strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); - printf(FMT("%-19s ","-> %s"), buf); + switch (family) { + case AF_INET: + print_ipv4_addr(cs, format); + break; + case AF_INET6: + print_ipv6_addr(cs, format); + break; } if (format & FMT_NOTABLE) fputs(" ", stdout); #ifdef IPT_F_GOTO - if(cs->fw.ip.flags & IPT_F_GOTO) + if(flags & IPT_F_GOTO) printf("[goto] "); #endif diff --git a/iptables/nft.h b/iptables/nft.h index aed2498b..1bd9ccce 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -4,6 +4,7 @@ #include "xshared.h" struct nft_handle { + int family; struct mnl_socket *nl; uint32_t portid; uint32_t seq; diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 30ea813c..e83eacc3 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -161,7 +161,9 @@ static void add_param_to_argv(char *parsestart) int xtables_restore_main(int argc, char *argv[]) { - struct nft_handle h; + struct nft_handle h = { + .family = AF_INET, /* default to IPv4 */ + }; char buffer[10240]; int c; char curtable[XT_TABLE_MAXNAMELEN + 1]; diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 046c948d..05d06b1e 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -76,7 +76,9 @@ int xtables_save_main(int argc, char *argv[]) { const char *tablename = NULL; - struct nft_handle h; + struct nft_handle h = { + .family = AF_INET, /* default to AF_INET */ + }; int c; xtables_globals.program_name = "xtables-save"; diff --git a/iptables/xtables.c b/iptables/xtables.c index d6045906..9c59b7d8 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -204,6 +204,20 @@ enum { IPT_DOTTED_MASK }; +struct addr_mask { + union { + struct in_addr *v4; + struct in6_addr *v6; + } addr; + + unsigned int naddrs; + + union { + struct in_addr *v4; + struct in6_addr *v6; + } mask; +}; + static void __attribute__((noreturn)) exit_tryhelp(int status) { @@ -370,6 +384,15 @@ add_command(unsigned int *cmd, const int newcmd, const int othercmds, * return global static data. */ +/* These are invalid numbers as upper layer protocol */ +static int is_exthdr(uint16_t proto) +{ + return (proto == IPPROTO_ROUTING || + proto == IPPROTO_FRAGMENT || + proto == IPPROTO_AH || + proto == IPPROTO_DSTOPTS); +} + /* Christophe Burki wants `-p 6' to imply `-m tcp'. */ /* Can't be zero. */ static int @@ -430,26 +453,38 @@ static int add_entry(const char *chain, const char *table, struct iptables_command_state *cs, - unsigned int nsaddrs, - const struct in_addr saddrs[], - const struct in_addr smasks[], - unsigned int ndaddrs, - const struct in_addr daddrs[], - const struct in_addr dmasks[], + int family, + const struct addr_mask s, + const struct addr_mask d, bool verbose, struct nft_handle *h, bool append) { unsigned int i, j; int ret = 1; - for (i = 0; i < nsaddrs; i++) { - cs->fw.ip.src.s_addr = saddrs[i].s_addr; - cs->fw.ip.smsk.s_addr = smasks[i].s_addr; - for (j = 0; j < ndaddrs; j++) { - cs->fw.ip.dst.s_addr = daddrs[j].s_addr; - cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; + for (i = 0; i < s.naddrs; i++) { + if (family == AF_INET) { + cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr; + for (j = 0; j < d.naddrs; j++) { + cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr; - ret = nft_rule_add(h, chain, table, - cs, append, 0, verbose); + ret = nft_rule_add(h, chain, table, + cs, append, 0, verbose); + } + } else if (family == AF_INET6) { + memcpy(&cs->fw6.ipv6.src, + &s.addr.v6[i], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.smsk, + &s.mask.v6[i], sizeof(struct in6_addr)); + for (j = 0; j < d.naddrs; j++) { + memcpy(&cs->fw6.ipv6.dst, + &d.addr.v6[j], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dmsk, + &d.mask.v6[j], sizeof(struct in6_addr)); + ret = nft_rule_add(h, chain, table, + cs, append, 0, verbose); + } } } @@ -460,14 +495,23 @@ static int replace_entry(const char *chain, const char *table, struct iptables_command_state *cs, unsigned int rulenum, - const struct in_addr *saddr, const struct in_addr *smask, - const struct in_addr *daddr, const struct in_addr *dmask, + int family, + const struct addr_mask s, + const struct addr_mask d, bool verbose, struct nft_handle *h) { - cs->fw.ip.src.s_addr = saddr->s_addr; - cs->fw.ip.dst.s_addr = daddr->s_addr; - cs->fw.ip.smsk.s_addr = smask->s_addr; - cs->fw.ip.dmsk.s_addr = dmask->s_addr; + if (family == AF_INET) { + cs->fw.ip.src.s_addr = s.addr.v4->s_addr; + cs->fw.ip.dst.s_addr = d.addr.v4->s_addr; + cs->fw.ip.smsk.s_addr = s.mask.v4->s_addr; + cs->fw.ip.dmsk.s_addr = d.mask.v4->s_addr; + } else if (family == AF_INET6) { + memcpy(&cs->fw6.ipv6.src, s.addr.v6, sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dst, d.addr.v6, sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.smsk, s.mask.v6, sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dmsk, d.mask.v6, sizeof(struct in6_addr)); + } else + return 1; return nft_rule_replace(h, chain, table, cs, rulenum, verbose); } @@ -475,25 +519,38 @@ replace_entry(const char *chain, const char *table, static int delete_entry(const char *chain, const char *table, struct iptables_command_state *cs, - unsigned int nsaddrs, - const struct in_addr saddrs[], - const struct in_addr smasks[], - unsigned int ndaddrs, - const struct in_addr daddrs[], - const struct in_addr dmasks[], + int family, + const struct addr_mask s, + const struct addr_mask d, bool verbose, struct nft_handle *h) { unsigned int i, j; int ret = 1; - for (i = 0; i < nsaddrs; i++) { - cs->fw.ip.src.s_addr = saddrs[i].s_addr; - cs->fw.ip.smsk.s_addr = smasks[i].s_addr; - for (j = 0; j < ndaddrs; j++) { - cs->fw.ip.dst.s_addr = daddrs[j].s_addr; - cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; - ret = nft_rule_delete(h, chain, table, cs, verbose); + for (i = 0; i < s.naddrs; i++) { + if (family == AF_INET) { + cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr; + for (j = 0; j < d.naddrs; j++) { + cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr; + ret = nft_rule_delete(h, chain, + table, cs, verbose); + } + } else if (family == AF_INET6) { + memcpy(&cs->fw6.ipv6.src, + &s.addr.v6[i], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.smsk, + &s.mask.v6[i], sizeof(struct in6_addr)); + for (j = 0; j < d.naddrs; j++) { + memcpy(&cs->fw6.ipv6.dst, + &d.addr.v6[j], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dmsk, + &d.mask.v6[j], sizeof(struct in6_addr)); + ret = nft_rule_delete(h, chain, + table, cs, verbose); + } } } @@ -503,21 +560,37 @@ delete_entry(const char *chain, const char *table, static int check_entry(const char *chain, const char *table, struct iptables_command_state *cs, - unsigned int nsaddrs, const struct in_addr *saddrs, - const struct in_addr *smasks, unsigned int ndaddrs, - const struct in_addr *daddrs, const struct in_addr *dmasks, + int family, + const struct addr_mask s, + const struct addr_mask d, bool verbose, struct nft_handle *h) { unsigned int i, j; int ret = 1; - for (i = 0; i < nsaddrs; i++) { - cs->fw.ip.src.s_addr = saddrs[i].s_addr; - cs->fw.ip.smsk.s_addr = smasks[i].s_addr; - for (j = 0; j < ndaddrs; j++) { - cs->fw.ip.dst.s_addr = daddrs[j].s_addr; - cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; - ret = nft_rule_check(h, chain, table, cs, verbose); + for (i = 0; i < s.naddrs; i++) { + if (family == AF_INET) { + cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr; + for (j = 0; j < d.naddrs; j++) { + cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr; + ret = nft_rule_check(h, chain, + table, cs, verbose); + } + } else if (family == AF_INET6) { + memcpy(&cs->fw6.ipv6.src, + &s.addr.v6[i], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.smsk, + &s.mask.v6[i], sizeof(struct in6_addr)); + for (j = 0; j < d.naddrs; j++) { + memcpy(&cs->fw6.ipv6.dst, + &d.addr.v6[j], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dmsk, + &d.mask.v6[j], sizeof(struct in6_addr)); + ret = nft_rule_check(h, chain, + table, cs, verbose); + } } } @@ -673,9 +746,8 @@ static void command_match(struct iptables_command_state *cs) int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) { struct iptables_command_state cs; - unsigned int nsaddrs = 0, ndaddrs = 0; - struct in_addr *saddrs = NULL, *smasks = NULL; - struct in_addr *daddrs = NULL, *dmasks = NULL; + struct addr_mask s; + struct addr_mask d; int verbose = 0; const char *chain = NULL; @@ -687,7 +759,21 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) struct xtables_match *m; struct xtables_rule_match *matchp; struct xtables_target *t; - unsigned long long cnt; + unsigned long long pcnt_cnt = 0, bcnt_cnt = 0; + + int family = AF_INET; + u_int16_t proto = 0; + u_int8_t flags = 0, invflags = 0; + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + bool goto_set = false; + + memset(&s, 0, sizeof(s)); + memset(&d, 0, sizeof(d)); + memset(iniface, 0, sizeof(iniface)); + memset(outiface, 0, sizeof(outiface)); + memset(iniface_mask, 0, sizeof(iniface_mask)); + memset(outiface_mask, 0, sizeof(outiface_mask)); memset(&cs, 0, sizeof(cs)); cs.jumpto = ""; @@ -877,7 +963,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) * Option selection */ case 'p': - set_option(&cs.options, OPT_PROTOCOL, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_PROTOCOL, &invflags, cs.invert); /* Canonicalize into lower case */ @@ -885,31 +971,30 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) *cs.protocol = tolower(*cs.protocol); cs.protocol = optarg; - cs.fw.ip.proto = xtables_parse_protocol(cs.protocol); + proto = xtables_parse_protocol(cs.protocol); - if (cs.fw.ip.proto == 0 - && (cs.fw.ip.invflags & XT_INV_PROTO)) + if (proto == 0 && (invflags & XT_INV_PROTO)) xtables_error(PARAMETER_PROBLEM, "rule would never match protocol"); break; case 's': - set_option(&cs.options, OPT_SOURCE, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_SOURCE, &invflags, cs.invert); shostnetworkmask = optarg; break; case 'd': - set_option(&cs.options, OPT_DESTINATION, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_DESTINATION, &invflags, cs.invert); dhostnetworkmask = optarg; break; #ifdef IPT_F_GOTO case 'g': - set_option(&cs.options, OPT_JUMP, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_JUMP, &invflags, cs.invert); - cs.fw.ip.flags |= IPT_F_GOTO; + goto_set = true; cs.jumpto = parse_target(optarg); break; #endif @@ -924,11 +1009,11 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "Empty interface is likely to be " "undesired"); - set_option(&cs.options, OPT_VIANAMEIN, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_VIANAMEIN, &invflags, cs.invert); xtables_parse_interface(optarg, - cs.fw.ip.iniface, - cs.fw.ip.iniface_mask); + iniface, + iniface_mask); break; case 'o': @@ -936,23 +1021,28 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "Empty interface is likely to be " "undesired"); - set_option(&cs.options, OPT_VIANAMEOUT, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_VIANAMEOUT, &invflags, cs.invert); xtables_parse_interface(optarg, - cs.fw.ip.outiface, - cs.fw.ip.outiface_mask); + outiface, + outiface_mask); break; case 'f': - set_option(&cs.options, OPT_FRAGMENT, &cs.fw.ip.invflags, + if (family == AF_INET6) { + xtables_error(PARAMETER_PROBLEM, + "`-f' is not supported in IPv6, " + "use -m frag instead"); + } + set_option(&cs.options, OPT_FRAGMENT, &invflags, cs.invert); - cs.fw.ip.flags |= IPT_F_FRAG; + flags |= IPT_F_FRAG; break; case 'v': if (!verbose) set_option(&cs.options, OPT_VERBOSE, - &cs.fw.ip.invflags, cs.invert); + &invflags, cs.invert); verbose++; break; @@ -961,7 +1051,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) break; case 'n': - set_option(&cs.options, OPT_NUMERIC, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_NUMERIC, &invflags, cs.invert); break; @@ -973,7 +1063,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) break; case 'x': - set_option(&cs.options, OPT_EXPANDED, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_EXPANDED, &invflags, cs.invert); break; @@ -986,7 +1076,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) exit(0); case '0': - set_option(&cs.options, OPT_LINENUMBERS, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_LINENUMBERS, &invflags, cs.invert); break; @@ -996,7 +1086,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) case 'c': - set_option(&cs.options, OPT_COUNTERS, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_COUNTERS, &invflags, cs.invert); pcnt = optarg; bcnt = strchr(pcnt + 1, ','); @@ -1010,29 +1100,25 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) "-%c requires packet and byte counter", opt2char(OPT_COUNTERS)); - if (sscanf(pcnt, "%llu", &cnt) != 1) + if (sscanf(pcnt, "%llu", &pcnt_cnt) != 1) xtables_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); - cs.counters.pcnt = cnt; - if (sscanf(bcnt, "%llu", &cnt) != 1) + if (sscanf(bcnt, "%llu", &bcnt_cnt) != 1) xtables_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(OPT_COUNTERS)); - cs.counters.bcnt = cnt; break; case '4': - /* This is indeed the IPv4 iptables */ + if (family != AF_INET) + exit_tryhelp(2); break; case '6': - /* This is not the IPv6 ip6tables */ - if (line != -1) - return 1; /* success: line ignored */ - fprintf(stderr, "This is the IPv4 version of iptables.\n"); - exit_tryhelp(2); + family = AF_INET6; + break; case 1: /* non option */ if (optarg[0] == '!' && optarg[1] == '\0') { @@ -1079,27 +1165,109 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "nothing appropriate following !"); - if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND | CMD_CHECK)) { - if (!(cs.options & OPT_DESTINATION)) - dhostnetworkmask = "0.0.0.0/0"; - if (!(cs.options & OPT_SOURCE)) - shostnetworkmask = "0.0.0.0/0"; - } + switch (family) { + case AF_INET: + cs.fw.ip.proto = proto; + cs.fw.ip.invflags = invflags; + cs.fw.ip.flags = flags; - if (shostnetworkmask) - xtables_ipparse_multiple(shostnetworkmask, &saddrs, - &smasks, &nsaddrs); + strncpy(cs.fw.ip.iniface, iniface, IFNAMSIZ); + memcpy(cs.fw.ip.iniface_mask, + iniface_mask, IFNAMSIZ*sizeof(unsigned char)); - if (dhostnetworkmask) - xtables_ipparse_multiple(dhostnetworkmask, &daddrs, - &dmasks, &ndaddrs); + strncpy(cs.fw.ip.outiface, outiface, IFNAMSIZ); + memcpy(cs.fw.ip.outiface_mask, + outiface_mask, IFNAMSIZ*sizeof(unsigned char)); - if ((nsaddrs > 1 || ndaddrs > 1) && - (cs.fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) - xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple" - " source or destination IP addresses"); + if (goto_set) + cs.fw.ip.flags |= IPT_F_GOTO; - if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) + cs.counters.pcnt = pcnt_cnt; + cs.counters.bcnt = bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs.options & OPT_DESTINATION)) + dhostnetworkmask = "0.0.0.0/0"; + if (!(cs.options & OPT_SOURCE)) + shostnetworkmask = "0.0.0.0/0"; + } + + if (shostnetworkmask) + xtables_ipparse_multiple(shostnetworkmask, &s.addr.v4, + &s.mask.v4, &s.naddrs); + if (dhostnetworkmask) + xtables_ipparse_multiple(dhostnetworkmask, &d.addr.v4, + &d.mask.v4, &d.naddrs); + + if ((s.naddrs > 1 || d.naddrs > 1) && + (cs.fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, + "! not allowed with multiple" + " source or destination IP addresses"); + break; + case AF_INET6: + if (proto != 0) + flags |= IP6T_F_PROTO; + + cs.fw6.ipv6.proto = proto; + cs.fw6.ipv6.invflags = invflags; + cs.fw6.ipv6.flags = flags; + + if (flags & IPT_F_FRAG) + xtables_error(PARAMETER_PROBLEM, + "-f is not valid on IPv6"); + + if (is_exthdr(cs.fw6.ipv6.proto) + && (cs.fw6.ipv6.invflags & XT_INV_PROTO) == 0) + fprintf(stderr, + "Warning: never matched protocol: %s. " + "use extension match instead.\n", + cs.protocol); + + strncpy(cs.fw6.ipv6.iniface, iniface, IFNAMSIZ); + memcpy(cs.fw6.ipv6.iniface_mask, + iniface_mask, IFNAMSIZ*sizeof(unsigned char)); + + strncpy(cs.fw6.ipv6.outiface, outiface, IFNAMSIZ); + memcpy(cs.fw6.ipv6.outiface_mask, + outiface_mask, IFNAMSIZ*sizeof(unsigned char)); + + if (goto_set) + cs.fw6.ipv6.flags |= IP6T_F_GOTO; + + cs.fw6.counters.pcnt = pcnt_cnt; + cs.fw6.counters.bcnt = bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs.options & OPT_DESTINATION)) + dhostnetworkmask = "::0/0"; + if (!(cs.options & OPT_SOURCE)) + shostnetworkmask = "::0/0"; + } + + if (shostnetworkmask) + xtables_ip6parse_multiple(shostnetworkmask, &s.addr.v6, + &s.mask.v6, &s.naddrs); + if (dhostnetworkmask) + xtables_ip6parse_multiple(dhostnetworkmask, &d.addr.v6, + &d.mask.v6, &d.naddrs); + + if ((s.naddrs > 1 || d.naddrs > 1) && + (cs.fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, + "! not allowed with multiple" + " source or destination IP addresses"); + break; + default: + exit_tryhelp(2); + break; + } + + h->family = family; + + if (command == CMD_REPLACE && (s.naddrs != 1 || d.naddrs != 1)) xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " "specify a unique address"); @@ -1144,39 +1312,30 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) switch (command) { case CMD_APPEND: - ret = add_entry(chain, *table, &cs, - nsaddrs, saddrs, smasks, - ndaddrs, daddrs, dmasks, - cs.options&OPT_VERBOSE, + ret = add_entry(chain, *table, &cs, family, + s, d, cs.options&OPT_VERBOSE, h, true); break; case CMD_DELETE: - ret = delete_entry(chain, *table, &cs, - nsaddrs, saddrs, smasks, - ndaddrs, daddrs, dmasks, - cs.options&OPT_VERBOSE, h); + ret = delete_entry(chain, *table, &cs, family, + s, d, cs.options&OPT_VERBOSE, h); break; case CMD_DELETE_NUM: ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose); break; case CMD_CHECK: - ret = check_entry(chain, *table, &cs, - nsaddrs, saddrs, smasks, - ndaddrs, daddrs, dmasks, - cs.options&OPT_VERBOSE, h); + ret = check_entry(chain, *table, &cs, family, + s, d, cs.options&OPT_VERBOSE, h); break; case CMD_REPLACE: /* FIXME replace at rulenum */ ret = replace_entry(chain, *table, &cs, rulenum - 1, - saddrs, smasks, daddrs, dmasks, - cs.options&OPT_VERBOSE, h); + family, s, d, cs.options&OPT_VERBOSE, h); break; case CMD_INSERT: /* FIXME insert at rulenum */ - ret = add_entry(chain, *table, &cs, - nsaddrs, saddrs, smasks, - ndaddrs, daddrs, dmasks, - cs.options&OPT_VERBOSE, h, false); + ret = add_entry(chain, *table, &cs, family, + s, d, cs.options&OPT_VERBOSE, h, false); break; case CMD_FLUSH: ret = nft_rule_flush(h, chain, *table); @@ -1242,10 +1401,17 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) clear_rule_matches(&cs.matches); - free(saddrs); - free(smasks); - free(daddrs); - free(dmasks); + if (family == AF_INET) { + free(s.addr.v4); + free(s.mask.v4); + free(d.addr.v4); + free(d.mask.v4); + } else if (family == AF_INET6) { + free(s.addr.v6); + free(s.mask.v6); + free(d.addr.v6); + free(d.mask.v6); + } xtables_free_opts(1); return ret; -- cgit v1.2.3 From 3bbe7c1a0a2a3bab261aeb00cf4c5adcc96bf109 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 20 Jan 2013 20:18:02 +0100 Subject: xtables-restore: fix custom user chain restoration Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-restore.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index e83eacc3..f6009776 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -331,20 +331,29 @@ xtables_restore_main(int argc, char *argv[]) "for chain '%s'\n", chain); } - + if (nft_chain_set(&h, curtable, chain, policy, &count) < 0) { + xtables_error(OTHER_PROBLEM, + "Can't set policy `%s'" + " on `%s' line %u: %s\n", + policy, chain, line, + ops->strerror(errno)); + } DEBUGP("Setting policy of chain %s to %s\n", - chain, policy); - } + chain, policy); + ret = 1; - if (nft_chain_set(&h, curtable, chain, policy, &count) < 0) { - xtables_error(OTHER_PROBLEM, - "Can't set policy `%s'" - " on `%s' line %u: %s\n", - policy, chain, line, - ops->strerror(errno)); - } + } else { + if (nft_chain_user_add(&h, chain, curtable) < 0) { + if (errno == EEXIST) + continue; - ret = 1; + xtables_error(PARAMETER_PROBLEM, + "cannot create chain " + "'%s' (%s)\n", chain, + strerror(errno)); + } + continue; + } } else if (in_table) { int a; -- cgit v1.2.3 From 8ebee8c46101914b269afe94e772321e5ee09c3f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 20 Jan 2013 20:24:36 +0100 Subject: xtables: fix compilation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xtables-standalone.c: In function ‘xtables_main’: xtables-standalone.c:64:2: warning: implicit declaration of function ‘do_commandx’ [-Wimplicit-function-declaration] Signed-off-by: Pablo Neira Ayuso --- iptables/nft.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/iptables/nft.h b/iptables/nft.h index 1bd9ccce..00216681 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -62,4 +62,7 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt); */ const char *nft_strerror(int err); +/* For xtables.c */ +int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table); + #endif -- cgit v1.2.3 From 0aad20f3979e3b6becd40e4ed5bba8d09d90706e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 20 Jan 2013 22:32:43 +0100 Subject: xtables: purge out user-define chains from the kernel xtables-restore has to purge out user-defined chains that are not defined in the configuration file. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 63 +++++++++++++++++++++++++++++++++++++++------- iptables/nft.h | 3 +++ iptables/xtables-restore.c | 23 ++++++++++++++--- 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 43b13deb..fd19ff55 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1801,19 +1801,13 @@ err: return ret == 0 ? 1 : 0; } -static struct nft_chain * -nft_chain_find(struct nft_handle *h, const char *table, const char *chain) +struct nft_chain * +nft_chain_list_find(struct nft_handle *h, struct nft_chain_list *list, + const char *table, const char *chain) { - struct nft_chain_list *list; struct nft_chain_list_iter *iter; struct nft_chain *c; - list = nft_chain_list_get(h); - if (list == NULL) { - DEBUGP("cannot allocate chain list\n"); - return NULL; - } - iter = nft_chain_list_iter_create(list); if (iter == NULL) { DEBUGP("cannot allocate rule list iterator\n"); @@ -1833,13 +1827,29 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain) if (strcmp(chain, chain_name) != 0) goto next; + nft_chain_list_iter_destroy(iter); return c; next: c = nft_chain_list_iter_next(iter); } + nft_chain_list_iter_destroy(iter); return NULL; } +static struct nft_chain * +nft_chain_find(struct nft_handle *h, const char *table, const char *chain) +{ + struct nft_chain_list *list; + + list = nft_chain_list_get(h); + if (list == NULL) { + DEBUGP("cannot allocate chain list\n"); + return NULL; + } + + return nft_chain_list_find(h, list, table, chain); +} + int nft_chain_user_rename(struct nft_handle *h,const char *chain, const char *table, const char *newname) { @@ -2006,6 +2016,41 @@ err: return ret == 0 ? 1 : 0; } +int nft_table_purge_chains(struct nft_handle *h, const char *this_table, + struct nft_chain_list *chain_list) +{ + struct nft_chain_list_iter *iter; + struct nft_chain *chain_obj; + + iter = nft_chain_list_iter_create(chain_list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + chain_obj = nft_chain_list_iter_next(iter); + while (chain_obj != NULL) { + const char *table = + nft_chain_attr_get_str(chain_obj, NFT_CHAIN_ATTR_TABLE); + + if (strcmp(this_table, table) != 0) + goto next; + + if (nft_chain_builtin(chain_obj)) + goto next; + + if ( __nft_chain_del(h, chain_obj) < 0) { + if (errno != EBUSY) + return -1; + } +next: + chain_obj = nft_chain_list_iter_next(iter); + } + nft_chain_list_iter_destroy(iter); + + return 0; +} + static inline int match_different(const struct xt_entry_match *a, const unsigned char *a_elems, diff --git a/iptables/nft.h b/iptables/nft.h index 00216681..f7ed0a38 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -17,12 +17,14 @@ void nft_fini(struct nft_handle *h); * Operations with tables. */ struct nft_table; +struct nft_chain_list; int nft_table_add(struct nft_handle *h, const struct nft_table *t); int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, bool counters), bool counters); bool nft_table_find(struct nft_handle *h, const char *tablename); int nft_table_set_dormant(struct nft_handle *h, const char *table); int nft_table_wake_dormant(struct nft_handle *h, const char *table); +int nft_table_purge_chains(struct nft_handle *h, const char *table, struct nft_chain_list *list); /* * Operations with chains. @@ -32,6 +34,7 @@ struct nft_chain; int nft_chain_add(struct nft_handle *h, const struct nft_chain *c); int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters); struct nft_chain_list *nft_chain_dump(struct nft_handle *h); +struct nft_chain *nft_chain_list_find(struct nft_handle *h, struct nft_chain_list *list, const char *table, const char *chain); int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, const char *table); int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table); int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table); diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index f6009776..9778a9f7 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -16,6 +16,7 @@ #include "libiptc/libiptc.h" #include "xtables-multi.h" #include "nft.h" +#include #ifdef DEBUG #define DEBUGP(x, args...) fprintf(stderr, x, ## args) @@ -171,6 +172,8 @@ xtables_restore_main(int argc, char *argv[]) int in_table = 0, testing = 0; const char *tablename = NULL; const struct xtc_ops *ops = &iptc_ops; + struct nft_chain_list *chain_list; + struct nft_chain *chain_obj; line = 0; @@ -233,6 +236,10 @@ xtables_restore_main(int argc, char *argv[]) } else in = stdin; + chain_list = nft_chain_dump(&h); + if (chain_list == NULL) + xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n"); + /* Grab standard input. */ while (fgets(buffer, sizeof(buffer), in)) { int ret = 0; @@ -259,6 +266,10 @@ xtables_restore_main(int argc, char *argv[]) ret = 1; } in_table = 0; + + /* Purge out unused chains in this table */ + nft_table_purge_chains(&h, curtable, chain_list); + } else if ((buffer[0] == '*') && (!in_table)) { /* New table */ char *table; @@ -282,10 +293,6 @@ xtables_restore_main(int argc, char *argv[]) DEBUGP("Cleaning all chains of table '%s'\n", table); nft_rule_flush(&h, NULL, table); - - DEBUGP("Deleting all user-defined chains " - "of table '%s'\n", table); - nft_chain_user_del(&h, NULL, table); } ret = 1; @@ -305,6 +312,14 @@ xtables_restore_main(int argc, char *argv[]) exit(1); } + chain_obj = nft_chain_list_find(&h, chain_list, + curtable, chain); + /* This chain has been found, delete from list. Later + * on, unvisited chains will be purged out. + */ + if (chain_obj != NULL) + nft_chain_list_del(chain_obj); + if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN) xtables_error(PARAMETER_PROBLEM, "Invalid chain name `%s' " -- cgit v1.2.3 From 9e62dc8637f210cdeaed784396fecab9b6e5f043 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 20 Jan 2013 20:19:20 +0100 Subject: xtables-restore: support atomic commit Use new services in nf_tables to support atomic commit. Commit per table, although we support global commit at once, call commit for each table to emulate iptables-restore behaviour by now. Keep table dormant/wake up code in iptables/nft.c as it can be used in the future. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 8 ++++++ iptables/nft.c | 56 +++++++++++++++++++++++++++++++++++++ iptables/nft.h | 7 +++++ iptables/xtables-restore.c | 22 +++++++++++---- iptables/xtables-standalone.c | 2 ++ 5 files changed, 90 insertions(+), 5 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index bdab3f2e..5385bf32 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -35,6 +35,8 @@ enum nf_tables_msg_types { NFT_MSG_NEWSETELEM, NFT_MSG_GETSETELEM, NFT_MSG_DELSETELEM, + NFT_MSG_COMMIT, + NFT_MSG_ABORT, NFT_MSG_MAX, }; @@ -83,12 +85,18 @@ enum nft_chain_attributes { }; #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) +enum { + NFT_RULE_F_COMMIT = (1 << 0), + NFT_RULE_F_MASK = NFT_RULE_F_COMMIT, +}; + enum nft_rule_attributes { NFTA_RULE_UNSPEC, NFTA_RULE_TABLE, NFTA_RULE_CHAIN, NFTA_RULE_HANDLE, NFTA_RULE_EXPRESSIONS, + NFTA_RULE_FLAGS, __NFTA_RULE_MAX }; #define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) diff --git a/iptables/nft.c b/iptables/nft.c index fd19ff55..f42e4377 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -946,6 +946,10 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, flags |= NLM_F_REPLACE; } + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, flags, h->seq); @@ -1626,6 +1630,11 @@ __nft_rule_flush(struct nft_handle *h, const char *table, const char *chain) nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } + /* Delete all rules in this table + chain */ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family, NLM_F_ACK, h->seq); @@ -2773,6 +2782,10 @@ int nft_rule_delete(struct nft_handle *h, const char *chain, if (r != NULL) { ret = 1; + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } DEBUGP("deleting rule\n"); __nft_rule_del(h, r); } else @@ -2802,6 +2815,10 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain, if (r != NULL) { ret = 1; + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } DEBUGP("deleting rule by number %d\n", rulenum); __nft_rule_del(h, r); } else @@ -2834,6 +2851,10 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, (unsigned long long) nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE)); + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } ret = nft_rule_add(h, chain, table, cs, true, nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE), verbose); @@ -3435,6 +3456,41 @@ next: return 1; } +static int nft_action(struct nft_handle *h, int type) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t seq; + int ret; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES<< 8) | type; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = 0; + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) { + if (errno != EEXIST) + perror("mnl-talk:nft_commit"); + } + return ret; +} + +int nft_commit(struct nft_handle *h) +{ + return nft_action(h, NFT_MSG_COMMIT); +} + +int nft_abort(struct nft_handle *h) +{ + return nft_action(h, NFT_MSG_ABORT); +} + int nft_compatible_revision(const char *name, uint8_t rev, int opt) { struct mnl_socket *nl; diff --git a/iptables/nft.h b/iptables/nft.h index f7ed0a38..834fff0d 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -8,6 +8,7 @@ struct nft_handle { struct mnl_socket *nl; uint32_t portid; uint32_t seq; + bool commit; }; int nft_init(struct nft_handle *h); @@ -55,6 +56,12 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *tabl int nft_rule_save(struct nft_handle *h, const char *table, bool counters); int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table); +/* + * global commit and abort + */ +int nft_commit(struct nft_handle *h); +int nft_abort(struct nft_handle *h); + /* * revision compatibility. */ diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 9778a9f7..ca9e0c05 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -164,6 +164,7 @@ xtables_restore_main(int argc, char *argv[]) { struct nft_handle h = { .family = AF_INET, /* default to IPv4 */ + .commit = true, }; char buffer[10240]; int c; @@ -253,10 +254,14 @@ xtables_restore_main(int argc, char *argv[]) continue; } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) { if (!testing) { - if (nft_table_wake_dormant(&h, curtable) < 0) { - fprintf(stderr, "Failed to wake up " - "dormant table `%s'\n", - curtable); + /* Commit per table, although we support + * global commit at once, stick by now to + * the existing behaviour. + */ + if (nft_commit(&h)) { + fprintf(stderr, "Failed to commit " + "table %s\n", + curtable); } DEBUGP("Calling commit\n"); ret = 1; @@ -288,7 +293,6 @@ xtables_restore_main(int argc, char *argv[]) if (tablename && (strcmp(tablename, table) != 0)) continue; - nft_table_set_dormant(&h, table); if (noflush == 0) { DEBUGP("Cleaning all chains of table '%s'\n", table); @@ -426,6 +430,14 @@ xtables_restore_main(int argc, char *argv[]) DEBUGP("argv[%u]: %s\n", a, newargv[a]); ret = do_commandx(&h, newargc, newargv, &newargv[2]); + if (ret < 0) { + ret = nft_abort(&h); + if (ret < 0) { + fprintf(stderr, "failed to abort " + "commit operation\n"); + } + exit(1); + } free_argv(); fflush(stdout); diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index f746c902..28416117 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -46,6 +46,8 @@ xtables_main(int argc, char *argv[]) char *table = "filter"; struct nft_handle h; + memset(&h, 0, sizeof(h)); + iptables_globals.program_name = "xtables"; ret = xtables_init_all(&xtables_globals, NFPROTO_IPV4); if (ret < 0) { -- cgit v1.2.3 From 2a87a024e1f77407e332086a4fa664e048280195 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 25 Jan 2013 16:04:36 +0100 Subject: xtables: nft: add protocol and flags for xtables over nf_tables Add protocol and flags for the compatibility layer. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 14 ++++++++++++++ iptables/nft.c | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 5385bf32..5f40dc05 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -97,10 +97,24 @@ enum nft_rule_attributes { NFTA_RULE_HANDLE, NFTA_RULE_EXPRESSIONS, NFTA_RULE_FLAGS, + NFTA_RULE_COMPAT, __NFTA_RULE_MAX }; #define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) +enum nft_rule_compat_flags { + NFT_RULE_COMPAT_F_INV = (1 << 1), + NFT_RULE_COMPAT_F_MASK = NFT_RULE_COMPAT_F_INV, +}; + +enum nft_rule_compat_attributes { + NFTA_RULE_COMPAT_UNSPEC, + NFTA_RULE_COMPAT_PROTO, + NFTA_RULE_COMPAT_FLAGS, + __NFTA_RULE_COMPAT_MAX +}; +#define NFTA_RULE_COMPAT_MAX (__NFTA_RULE_COMPAT_MAX - 1) + /** * enum nft_set_flags - nf_tables set flags * diff --git a/iptables/nft.c b/iptables/nft.c index f42e4377..c3d5d610 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -800,6 +800,13 @@ static void add_addr(struct nft_rule *r, int offset, add_cmp_ptr(r, op, data, len); } +static void add_compat(struct nft_rule *r, uint32_t proto, bool inv) +{ + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_PROTO, proto); + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_FLAGS, + inv ? NFT_RULE_COMPAT_F_INV : 0); +} + static void add_proto(struct nft_rule *r, int offset, size_t len, uint32_t proto, int invflags) { @@ -813,6 +820,7 @@ static void add_proto(struct nft_rule *r, int offset, size_t len, op = NFT_CMP_EQ; add_cmp_u32(r, proto, op); + add_compat(r, proto, invflags & XT_INV_PROTO); } int -- cgit v1.2.3 From 18af813cabf7b574dec86beedf0a335e5928eaaa Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 20 Jan 2013 23:23:29 +0100 Subject: xtables-restore: support test option `-t' You can now test if a rule-set is correct. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-restore.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index ca9e0c05..c62b0a9a 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -266,14 +266,20 @@ xtables_restore_main(int argc, char *argv[]) DEBUGP("Calling commit\n"); ret = 1; } else { - /* FIXME -t needs to be fixed */ + if (nft_abort(&h)) { + xtables_error(OTHER_PROBLEM, + "Failed to abort " + "commit in table %s\n", + curtable); + } DEBUGP("Not calling commit, testing\n"); ret = 1; } in_table = 0; /* Purge out unused chains in this table */ - nft_table_purge_chains(&h, curtable, chain_list); + if (!testing) + nft_table_purge_chains(&h, curtable, chain_list); } else if ((buffer[0] == '*') && (!in_table)) { /* New table */ -- cgit v1.2.3 From c51b85f995caebd41e6d063c8bcab513b305bcaa Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 27 Jan 2013 23:19:49 +0100 Subject: nft: fix crash if TRACE is used And any other match and target with no save function defined. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index c3d5d610..445d8f92 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1006,7 +1006,8 @@ static void nft_match_save(struct nft_rule_expr *expr) printf("-m %s", match->name); /* FIXME missing parameter */ - match->save(NULL, emu); + if (match->save) + match->save(NULL, emu); printf(" "); @@ -1049,7 +1050,8 @@ static void nft_target_save(struct nft_rule_expr *expr) printf("-j %s", target->name); /* FIXME missing parameter */ - target->save(NULL, emu); + if (target->save) + target->save(NULL, emu); free(emu); } -- cgit v1.2.3 From 5e6244b2ad70d7a59508aa9cf33efcd69bcde45c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 9 Feb 2013 18:27:04 +0100 Subject: xtables: ipv6: fix wrong error if -p is used shell$ xtables -6 -I INPUT -p tcp --dport 22 -j ACCEPT xtables v1.4.15: -f is not valid on IPv6 Try `xtables -h' or 'xtables --help' for more information. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index 9c59b7d8..328bc695 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -1214,10 +1214,6 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) cs.fw6.ipv6.invflags = invflags; cs.fw6.ipv6.flags = flags; - if (flags & IPT_F_FRAG) - xtables_error(PARAMETER_PROBLEM, - "-f is not valid on IPv6"); - if (is_exthdr(cs.fw6.ipv6.proto) && (cs.fw6.ipv6.invflags & XT_INV_PROTO) == 0) fprintf(stderr, -- cgit v1.2.3 From 946f22238350a1506b7fe57b2b27e9e5427f0583 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 9 Feb 2013 18:35:48 +0100 Subject: xtables: ipv6: add missing break in nft_parse_payload_ipv6 Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iptables/nft.c b/iptables/nft.c index 445d8f92..9c6056a4 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2352,6 +2352,7 @@ nft_parse_payload_ipv6(uint32_t offset, struct nft_rule_expr_iter *iter, cs->fw6.ipv6.proto = proto; if (inv) cs->fw6.ipv6.invflags |= IPT_INV_PROTO; + break; default: DEBUGP("unknown payload offset %d\n", offset); break; -- cgit v1.2.3 From af11340016320d73dc88fa377a2d2aa21173ba07 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 9 Feb 2013 18:44:34 +0100 Subject: xtables: ipv6: fix -D with -p Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iptables/nft.c b/iptables/nft.c index 9c6056a4..cef1f32e 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2349,6 +2349,7 @@ nft_parse_payload_ipv6(uint32_t offset, struct nft_rule_expr_iter *iter, break; case offsetof(struct ip6_hdr, ip6_nxt): get_cmp_data(iter, &proto, sizeof(proto), &inv); + cs->fw6.ipv6.flags |= IP6T_F_PROTO; cs->fw6.ipv6.proto = proto; if (inv) cs->fw6.ipv6.invflags |= IPT_INV_PROTO; -- cgit v1.2.3 From 1ff21a68502d67e056100da7e0da074467bc08ed Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 9 Feb 2013 18:22:13 +0100 Subject: add xtables-events Add new program to listen to rule updates: shell$ xtables-events -A INPUT -m state --state ESTABLISHED -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -D INPUT -p tcp -m tcp --dport 22 -j ACCEPT -D INPUT -m state --state ESTABLISHED -j ACCEPT You can use `-c' option to display counters. Signed-off-by: Pablo Neira Ayuso --- iptables/Makefile.am | 5 +- iptables/nft.c | 19 +++-- iptables/nft.h | 7 ++ iptables/xtables-events.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++ iptables/xtables-multi.c | 1 + iptables/xtables-multi.h | 1 + 6 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 iptables/xtables-events.c diff --git a/iptables/Makefile.am b/iptables/Makefile.am index 2b1f3fa4..2ed16960 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -31,7 +31,7 @@ if HAVE_LIBNFTABLES xtables_multi_SOURCES += xtables-save.c xtables-restore.c \ xtables-standalone.c xtables.c nft.c \ xtables-config-parser.y xtables-config-syntax.l \ - xtables-config.c + xtables-config.c xtables-events.c xtables_multi_LDADD += -lmnl -lnftables xtables_multi_CFLAGS += -DENABLE_NFTABLES # yacc and lex generate dirty code @@ -55,7 +55,8 @@ if ENABLE_IPV6 v6_sbin_links = ip6tables ip6tables-restore ip6tables-save endif if ENABLE_NFTABLES -x_sbin_links = xtables xtables-restore xtables-save xtables-config +x_sbin_links = xtables xtables-restore xtables-save xtables-config \ + xtables-events endif iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man diff --git a/iptables/nft.c b/iptables/nft.c index cef1f32e..097c28b9 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1385,13 +1385,22 @@ nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, } } -static void nft_rule_print_save(struct nft_rule *r, bool counters) +void +nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters) { struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; + const char *chain = nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); /* print chain name */ - printf("-A %s ", nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN)); + switch(type) { + case NFT_RULE_APPEND: + printf("-A %s ", chain); + break; + case NFT_RULE_DEL: + printf("-D %s ", chain); + break; + } iter = nft_rule_expr_iter_create(r); if (iter == NULL) @@ -1614,7 +1623,7 @@ int nft_rule_save(struct nft_handle *h, const char *table, bool counters) if (strcmp(table, rule_table) != 0) goto next; - nft_rule_print_save(r, counters); + nft_rule_print_save(r, NFT_RULE_APPEND, counters); next: r = nft_rule_list_iter_next(iter); @@ -2714,7 +2723,7 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, /* Delete by matching rule case */ DEBUGP("comparing with... "); #ifdef DEBUG_DEL - nft_rule_print_save(r, 0); + nft_rule_print_save(r, NFT_RULE_APPEND, 0); #endif nft_rule_to_iptables_command_state(r, &this); @@ -3378,7 +3387,7 @@ static void list_save(const struct iptables_command_state *cs, struct nft_rule *r, unsigned int num, unsigned int format) { - nft_rule_print_save(r, !(format & FMT_NOCOUNTS)); + nft_rule_print_save(r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS)); } static int diff --git a/iptables/nft.h b/iptables/nft.h index 834fff0d..3cffb777 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -56,6 +56,13 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *tabl int nft_rule_save(struct nft_handle *h, const char *table, bool counters); int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table); +enum nft_rule_print { + NFT_RULE_APPEND, + NFT_RULE_DEL, +}; + +void nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters); + /* * global commit and abort */ diff --git a/iptables/xtables-events.c b/iptables/xtables-events.c new file mode 100644 index 00000000..2600a250 --- /dev/null +++ b/iptables/xtables-events.c @@ -0,0 +1,208 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software has been sponsored by Sophos Astaro + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include "iptables.h" /* for xtables_globals */ +#include "xtables-multi.h" +#include "nft.h" + +static int table_cb(const struct nlmsghdr *nlh, int type) +{ + struct nft_table *t; + char buf[4096]; + + t = nft_table_alloc(); + if (t == NULL) { + perror("OOM"); + goto err; + } + + if (nft_table_nlmsg_parse(nlh, t) < 0) { + perror("nft_table_nlmsg_parse"); + goto err_free; + } + + nft_table_snprintf(buf, sizeof(buf), t, NFT_TABLE_O_DEFAULT, 0); + /* FIXME: define syntax to represent table events */ + printf("# [table: %s]\t%s", type == NFT_MSG_NEWTABLE ? "NEW" : "DEL", buf); + +err_free: + nft_table_free(t); +err: + return MNL_CB_OK; +} + +static bool counters; + +static int rule_cb(const struct nlmsghdr *nlh, int type) +{ + struct nft_rule *r; + + r = nft_rule_alloc(); + if (r == NULL) { + perror("OOM"); + goto err; + } + + if (nft_rule_nlmsg_parse(nlh, r) < 0) { + perror("nft_rule_nlmsg_parse"); + goto err_free; + } + + switch(nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)) { + case AF_INET: + printf("-4 "); + break; + case AF_INET6: + printf("-6 "); + break; + default: + break; + } + + nft_rule_print_save(r, type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND : + NFT_RULE_DEL, + counters); +err_free: + nft_rule_free(r); +err: + return MNL_CB_OK; +} + +static int chain_cb(const struct nlmsghdr *nlh, int type) +{ + struct nft_chain *t; + char buf[4096]; + + t = nft_chain_alloc(); + if (t == NULL) { + perror("OOM"); + goto err; + } + + if (nft_chain_nlmsg_parse(nlh, t) < 0) { + perror("nft_chain_nlmsg_parse"); + goto err_free; + } + + nft_chain_snprintf(buf, sizeof(buf), t, NFT_CHAIN_O_DEFAULT, 0); + /* FIXME: define syntax to represent chain events */ + printf("# [chain: %s]\t%s", type == NFT_MSG_NEWCHAIN ? "NEW" : "DEL", buf); + +err_free: + nft_chain_free(t); +err: + return MNL_CB_OK; +} + +static int events_cb(const struct nlmsghdr *nlh, void *data) +{ + int ret = MNL_CB_OK; + int type = nlh->nlmsg_type & 0xFF; + + switch(type) { + case NFT_MSG_NEWTABLE: + case NFT_MSG_DELTABLE: + ret = table_cb(nlh, type); + break; + case NFT_MSG_NEWCHAIN: + case NFT_MSG_DELCHAIN: + ret = chain_cb(nlh, type); + break; + case NFT_MSG_NEWRULE: + case NFT_MSG_DELRULE: + ret = rule_cb(nlh, type); + break; + } + + return ret; +} + +static const struct option options[] = { + {.name = "counters", .has_arg = false, .val = 'c'}, + {NULL}, +}; + +static void print_usage(const char *name, const char *version) +{ + fprintf(stderr, "Usage: %s [-c]\n" + " [ --counters ]\n", name); + exit(EXIT_FAILURE); +} + +int xtables_events_main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret, c; + + xtables_globals.program_name = "xtables-events"; + /* XXX xtables_init_all does several things we don't want */ + c = xtables_init_all(&xtables_globals, NFPROTO_IPV4); + if (c < 0) { + fprintf(stderr, "%s/%s Failed to initialize xtables\n", + xtables_globals.program_name, + xtables_globals.program_version); + exit(1); + } +#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) + init_extensions(); + init_extensions4(); +#endif + + while ((c = getopt_long(argc, argv, "c", options, NULL)) != -1) { + switch (c) { + case 'c': + counters = true; + break; + } + } + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, (1 << (NFNLGRP_NFTABLES-1)), MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, 0, 0, events_cb, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + mnl_socket_close(nl); + + return EXIT_SUCCESS; +} diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c index c1746434..5732ba38 100644 --- a/iptables/xtables-multi.c +++ b/iptables/xtables-multi.c @@ -41,6 +41,7 @@ static const struct subcommand multi_subcommands[] = { {"xtables-save", xtables_save_main}, {"xtables-restore", xtables_restore_main}, {"xtables-config", xtables_config_main}, + {"xtables-events", xtables_events_main}, #endif {NULL}, }; diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h index be2b3ad8..c609ea5b 100644 --- a/iptables/xtables-multi.h +++ b/iptables/xtables-multi.h @@ -6,5 +6,6 @@ extern int xtables_main(int, char **); extern int xtables_save_main(int, char **); extern int xtables_restore_main(int, char **); extern int xtables_config_main(int, char **); +extern int xtables_events_main(int, char **); #endif /* _XTABLES_MULTI_H */ -- cgit v1.2.3 From 077785df023ad8947d44d19769bc6d91e3917633 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Sat, 23 Feb 2013 17:50:31 +0100 Subject: nft: Split nft core to become family independant This makes nft core code independant from the family. Each family needs to implement and provide a struct nft_family_ops {}. This split will ease the future support of bridge and arp rules manipulations. [ updated header files and rebased upon the current tree --pablo ] Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/Makefile.am | 1 + iptables/nft-ipv4.c | 307 +++++++++++++++++ iptables/nft-ipv6.c | 217 ++++++++++++ iptables/nft-shared.c | 418 +++++++++++++++++++++++ iptables/nft-shared.h | 110 +++++++ iptables/nft.c | 896 +------------------------------------------------- iptables/nft.h | 2 + iptables/xtables.c | 19 +- 8 files changed, 1077 insertions(+), 893 deletions(-) create mode 100644 iptables/nft-ipv4.c create mode 100644 iptables/nft-ipv6.c create mode 100644 iptables/nft-shared.c create mode 100644 iptables/nft-shared.h diff --git a/iptables/Makefile.am b/iptables/Makefile.am index 2ed16960..11c501b9 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -30,6 +30,7 @@ if HAVE_LIBMNL if HAVE_LIBNFTABLES xtables_multi_SOURCES += xtables-save.c xtables-restore.c \ xtables-standalone.c xtables.c nft.c \ + nft-shared.c nft-ipv4.c nft-ipv6.c \ xtables-config-parser.y xtables-config-syntax.l \ xtables-config.c xtables-events.c xtables_multi_LDADD += -lmnl -lnftables diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c new file mode 100644 index 00000000..ac2b27b8 --- /dev/null +++ b/iptables/nft-ipv4.c @@ -0,0 +1,307 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * (C) 2013 by Tomasz Bursztyka + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include "nft-shared.h" + +static int nft_ipv4_add(struct nft_rule *r, struct iptables_command_state *cs) +{ + uint32_t op; + + if (cs->fw.ip.iniface[0] != '\0') + add_iniface(r, cs->fw.ip.iniface, cs->fw.ip.invflags); + + if (cs->fw.ip.outiface[0] != '\0') + add_outiface(r, cs->fw.ip.outiface, cs->fw.ip.invflags); + + if (cs->fw.ip.src.s_addr != 0) + add_addr(r, offsetof(struct iphdr, saddr), + &cs->fw.ip.src.s_addr, 4, cs->fw.ip.invflags); + + if (cs->fw.ip.dst.s_addr != 0) + add_addr(r, offsetof(struct iphdr, daddr), + &cs->fw.ip.dst.s_addr, 4, cs->fw.ip.invflags); + + if (cs->fw.ip.proto != 0) + add_proto(r, offsetof(struct iphdr, protocol), 1, + cs->fw.ip.proto, cs->fw.ip.invflags); + + if (cs->fw.ip.flags & IPT_F_FRAG) { + add_payload(r, offsetof(struct iphdr, frag_off), 2); + /* get the 13 bits that contain the fragment offset */ + add_bitwise_u16(r, 0x1fff, !0x1fff); + + /* if offset is non-zero, this is a fragment */ + if (cs->fw.ip.invflags & IPT_INV_FRAG) + op = NFT_CMP_EQ; + else + op = NFT_CMP_NEQ; + + add_cmp_u16(r, 0, op); + } + + return cs->fw.ip.flags; +} + +static bool nft_ipv4_is_same(const struct iptables_command_state *a, + const struct iptables_command_state *b) +{ + if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr + || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr + || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr + || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr + || a->fw.ip.proto != b->fw.ip.proto + || a->fw.ip.flags != b->fw.ip.flags + || a->fw.ip.invflags != b->fw.ip.invflags) { + DEBUGP("different src/dst/proto/flags/invflags\n"); + return false; + } + + return is_same_interfaces(a->fw.ip.iniface, a->fw.ip.outiface, + a->fw.ip.iniface_mask, a->fw.ip.outiface_mask, + b->fw.ip.iniface, b->fw.ip.outiface, + b->fw.ip.iniface_mask, b->fw.ip.outiface_mask); +} + +static void get_frag(struct nft_rule_expr_iter *iter, bool *inv) +{ + struct nft_rule_expr *e; + const char *name; + uint8_t op; + + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + /* we assume correct mask and xor */ + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + if (strcmp(name, "bitwise") != 0) { + DEBUGP("skipping no bitwise after payload\n"); + return; + } + + /* Now check for cmp */ + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + /* we assume correct data */ + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + if (strcmp(name, "cmp") != 0) { + DEBUGP("skipping no cmp after payload\n"); + return; + } + + op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); + if (op == NFT_CMP_EQ) + *inv = true; + else + *inv = false; +} + +static void print_frag(bool inv) +{ + if (inv) + printf("! -f "); + else + printf("-f "); +} + +static const char *mask_to_str(uint32_t mask) +{ + static char mask_str[sizeof("255.255.255.255")]; + uint32_t bits, hmask = ntohl(mask); + struct in_addr mask_addr = { + .s_addr = mask, + }; + int i; + + if (mask == 0xFFFFFFFFU) { + sprintf(mask_str, "32"); + return mask_str; + } + + i = 32; + bits = 0xFFFFFFFEU; + while (--i >= 0 && hmask != bits) + bits <<= 1; + if (i >= 0) + sprintf(mask_str, "%u", i); + else + sprintf(mask_str, "%s", inet_ntoa(mask_addr)); + + return mask_str; +} + +static void nft_ipv4_print_payload(struct nft_rule_expr *e, + struct nft_rule_expr_iter *iter) +{ + uint32_t offset; + bool inv; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + switch(offset) { + struct in_addr addr; + uint8_t proto; + + case offsetof(struct iphdr, saddr): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + if (inv) + printf("! -s %s/%s ", inet_ntoa(addr), + mask_to_str(0xffffffff)); + else + printf("-s %s/%s ", inet_ntoa(addr), + mask_to_str(0xffffffff)); + break; + case offsetof(struct iphdr, daddr): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + if (inv) + printf("! -d %s/%s ", inet_ntoa(addr), + mask_to_str(0xffffffff)); + else + printf("-d %s/%s ", inet_ntoa(addr), + mask_to_str(0xffffffff)); + break; + case offsetof(struct iphdr, protocol): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + print_proto(proto, inv); + break; + case offsetof(struct iphdr, frag_off): + get_frag(iter, &inv); + print_frag(inv); + break; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + +static void nft_ipv4_parse_meta(struct nft_rule_expr *e, uint8_t key, + struct iptables_command_state *cs) +{ + parse_meta(e, key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask, + cs->fw.ip.outiface, cs->fw.ip.outiface_mask, + &cs->fw.ip.invflags); +} + +static void nft_ipv4_parse_payload(struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs, + uint32_t offset) +{ + switch(offset) { + struct in_addr addr; + uint8_t proto; + bool inv; + + case offsetof(struct iphdr, saddr): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + cs->fw.ip.src.s_addr = addr.s_addr; + cs->fw.ip.smsk.s_addr = 0xffffffff; + if (inv) + cs->fw.ip.invflags |= IPT_INV_SRCIP; + break; + case offsetof(struct iphdr, daddr): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + cs->fw.ip.dst.s_addr = addr.s_addr; + cs->fw.ip.dmsk.s_addr = 0xffffffff; + if (inv) + cs->fw.ip.invflags |= IPT_INV_DSTIP; + break; + case offsetof(struct iphdr, protocol): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + cs->fw.ip.proto = proto; + if (inv) + cs->fw.ip.invflags |= IPT_INV_PROTO; + break; + case offsetof(struct iphdr, frag_off): + cs->fw.ip.flags |= IPT_F_FRAG; + get_frag(iter, &inv); + if (inv) + cs->fw.ip.invflags |= IPT_INV_FRAG; + break; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + +static void nft_ipv4_parse_immediate(struct iptables_command_state *cs) +{ + cs->fw.ip.flags |= IPT_F_GOTO; +} + +static void print_ipv4_addr(const struct iptables_command_state *cs, + unsigned int format) +{ + char buf[BUFSIZ]; + + fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src)); + else + strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src)); + strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); + printf(FMT("%-19s ","%s "), buf); + } + + fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst)); + else + strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst)); + strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); + printf(FMT("%-19s ","-> %s"), buf); + } +} + + +static uint8_t nft_ipv4_print_firewall(const struct iptables_command_state *cs, + const char *targname, unsigned int num, + unsigned int format) +{ + print_firewall_details(cs, targname, cs->fw.ip.flags, + cs->fw.ip.invflags, cs->fw.ip.proto, + cs->fw.ip.iniface, cs->fw.ip.outiface, + num, format); + + print_ipv4_addr(cs, format); + + return cs->fw.ip.flags; +} + +struct nft_family_ops nft_family_ops_ipv4 = { + .add = nft_ipv4_add, + .is_same = nft_ipv4_is_same, + .print_payload = nft_ipv4_print_payload, + .parse_meta = nft_ipv4_parse_meta, + .parse_payload = nft_ipv4_parse_payload, + .parse_immediate = nft_ipv4_parse_immediate, + .print_firewall = nft_ipv4_print_firewall, +}; diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c new file mode 100644 index 00000000..a2e80306 --- /dev/null +++ b/iptables/nft-ipv6.c @@ -0,0 +1,217 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * (C) 2013 by Tomasz Bursztyka + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include "nft-shared.h" + +static int nft_ipv6_add(struct nft_rule *r, struct iptables_command_state *cs) +{ + if (cs->fw6.ipv6.iniface[0] != '\0') + add_iniface(r, cs->fw6.ipv6.iniface, cs->fw6.ipv6.invflags); + + if (cs->fw6.ipv6.outiface[0] != '\0') + add_outiface(r, cs->fw6.ipv6.outiface, cs->fw6.ipv6.invflags); + + if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)) + add_addr(r, offsetof(struct ip6_hdr, ip6_src), + &cs->fw6.ipv6.src, 16, cs->fw6.ipv6.invflags); + + if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)) + add_addr(r, offsetof(struct ip6_hdr, ip6_dst), + &cs->fw6.ipv6.dst, 16, cs->fw6.ipv6.invflags); + + if (cs->fw6.ipv6.proto != 0) + add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1, + cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags); + + return cs->fw6.ipv6.flags; +} + +static bool nft_ipv6_is_same(const struct iptables_command_state *a, + const struct iptables_command_state *b) +{ + if (memcmp(a->fw6.ipv6.src.s6_addr, b->fw6.ipv6.src.s6_addr, + sizeof(struct in6_addr)) != 0 + || memcmp(a->fw6.ipv6.dst.s6_addr, b->fw6.ipv6.dst.s6_addr, + sizeof(struct in6_addr)) != 0 + || a->fw6.ipv6.proto != b->fw6.ipv6.proto + || a->fw6.ipv6.flags != b->fw6.ipv6.flags + || a->fw6.ipv6.invflags != b->fw6.ipv6.invflags) { + DEBUGP("different src/dst/proto/flags/invflags\n"); + return false; + } + + return is_same_interfaces(a->fw6.ipv6.iniface, a->fw6.ipv6.outiface, + a->fw6.ipv6.iniface_mask, + a->fw6.ipv6.outiface_mask, + b->fw6.ipv6.iniface, b->fw6.ipv6.outiface, + b->fw6.ipv6.iniface_mask, + b->fw6.ipv6.outiface_mask); +} + +static void nft_ipv6_print_payload(struct nft_rule_expr *e, + struct nft_rule_expr_iter *iter) +{ + uint32_t offset; + bool inv; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + switch (offset) { + char addr_str[INET6_ADDRSTRLEN]; + struct in6_addr addr; + uint8_t proto; + case offsetof(struct ip6_hdr, ip6_src): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); + + if (inv) + printf("! -s %s ", addr_str); + else + printf("-s %s ", addr_str); + + break; + case offsetof(struct ip6_hdr, ip6_dst): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); + + if (inv) + printf("! -d %s ", addr_str); + else + printf("-d %s ", addr_str); + + break; + case offsetof(struct ip6_hdr, ip6_nxt): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + print_proto(proto, inv); + break; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + +static void nft_ipv6_parse_meta(struct nft_rule_expr *e, uint8_t key, + struct iptables_command_state *cs) +{ + parse_meta(e, key, cs->fw6.ipv6.iniface, + cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface, + cs->fw6.ipv6.outiface_mask, &cs->fw6.ipv6.invflags); +} + +static void nft_ipv6_parse_payload(struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs, + uint32_t offset) +{ + switch (offset) { + struct in6_addr addr; + uint8_t proto; + bool inv; + + case offsetof(struct ip6_hdr, ip6_src): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr)); + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_SRCIP; + break; + case offsetof(struct ip6_hdr, ip6_dst): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr)); + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_DSTIP; + break; + case offsetof(struct ip6_hdr, ip6_nxt): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + cs->fw6.ipv6.flags |= IP6T_F_PROTO; + cs->fw6.ipv6.proto = proto; + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_PROTO; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + +static void nft_ipv6_parse_immediate(struct iptables_command_state *cs) +{ + cs->fw6.ipv6.flags |= IPT_F_GOTO; +} + +static void print_ipv6_addr(const struct iptables_command_state *cs, + unsigned int format) +{ + char buf[BUFSIZ]; + + fputc(cs->fw6.ipv6.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src) + && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, + xtables_ip6addr_to_numeric(&cs->fw6.ipv6.src)); + else + strcpy(buf, + xtables_ip6addr_to_anyname(&cs->fw6.ipv6.src)); + strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.smsk)); + printf(FMT("%-19s ","%s "), buf); + } + + + fputc(cs->fw6.ipv6.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst) + && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, + xtables_ip6addr_to_numeric(&cs->fw6.ipv6.dst)); + else + strcpy(buf, + xtables_ip6addr_to_anyname(&cs->fw6.ipv6.dst)); + strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.dmsk)); + printf(FMT("%-19s ","-> %s"), buf); + } +} + +static uint8_t nft_ipv6_print_firewall(const struct iptables_command_state *cs, + const char *targname, unsigned int num, + unsigned int format) +{ + print_firewall_details(cs, targname, cs->fw6.ipv6.flags, + cs->fw6.ipv6.invflags, cs->fw6.ipv6.proto, + cs->fw6.ipv6.iniface, cs->fw6.ipv6.outiface, + num, format); + + print_ipv6_addr(cs, format); + + return cs->fw6.ipv6.flags; +} + +struct nft_family_ops nft_family_ops_ipv6 = { + .add = nft_ipv6_add, + .is_same = nft_ipv6_is_same, + .print_payload = nft_ipv6_print_payload, + .parse_meta = nft_ipv6_parse_meta, + .parse_payload = nft_ipv6_parse_payload, + .parse_immediate = nft_ipv6_parse_immediate, + .print_firewall = nft_ipv6_print_firewall, +}; diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c new file mode 100644 index 00000000..c368f40b --- /dev/null +++ b/iptables/nft-shared.c @@ -0,0 +1,418 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * (C) 2013 by Tomasz Bursztyka + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "nft-shared.h" +#include "xshared.h" + +extern struct nft_family_ops nft_family_ops_ipv4; +extern struct nft_family_ops nft_family_ops_ipv6; + +void add_meta(struct nft_rule *r, uint32_t key) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("meta"); + if (expr == NULL) + return; + + nft_rule_expr_set_u32(expr, NFT_EXPR_META_KEY, key); + nft_rule_expr_set_u32(expr, NFT_EXPR_META_DREG, NFT_REG_1); + + nft_rule_add_expr(r, expr); +} + +void add_payload(struct nft_rule *r, int offset, int len) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("payload"); + if (expr == NULL) + return; + + nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_BASE, + NFT_PAYLOAD_NETWORK_HEADER); + nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_DREG, NFT_REG_1); + nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_OFFSET, offset); + nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_LEN, len); + + nft_rule_add_expr(r, expr); +} + +/* bitwise operation is = sreg & mask ^ xor */ +void add_bitwise_u16(struct nft_rule *r, int mask, int xor) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("bitwise"); + if (expr == NULL) + return; + + nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_SREG, NFT_REG_1); + nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_DREG, NFT_REG_1); + nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_LEN, sizeof(uint16_t)); + nft_rule_expr_set(expr, NFT_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t)); + nft_rule_expr_set(expr, NFT_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t)); + + nft_rule_add_expr(r, expr); +} + +void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len) +{ + struct nft_rule_expr *expr; + + expr = nft_rule_expr_alloc("cmp"); + if (expr == NULL) + return; + + nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_SREG, NFT_REG_1); + nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_OP, op); + nft_rule_expr_set(expr, NFT_EXPR_CMP_DATA, data, len); + + nft_rule_add_expr(r, expr); +} + +void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op) +{ + add_cmp_ptr(r, op, &val, sizeof(val)); +} + +void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op) +{ + add_cmp_ptr(r, op, &val, sizeof(val)); +} + +void add_iniface(struct nft_rule *r, char *iface, int invflags) +{ + int iface_len; + uint32_t op; + + iface_len = strlen(iface); + + if (invflags & IPT_INV_VIA_IN) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + if (iface[iface_len - 1] == '+') { + add_meta(r, NFT_META_IIFNAME); + add_cmp_ptr(r, op, iface, iface_len - 1); + } else { + add_meta(r, NFT_META_IIF); + add_cmp_u32(r, if_nametoindex(iface), op); + } +} + +void add_outiface(struct nft_rule *r, char *iface, int invflags) +{ + int iface_len; + uint32_t op; + + iface_len = strlen(iface); + + if (invflags & IPT_INV_VIA_OUT) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + if (iface[iface_len - 1] == '+') { + add_meta(r, NFT_META_OIFNAME); + add_cmp_ptr(r, op, iface, iface_len - 1); + } else { + add_meta(r, NFT_META_OIF); + add_cmp_u32(r, if_nametoindex(iface), op); + } +} + +void add_addr(struct nft_rule *r, int offset, + void *data, size_t len, int invflags) +{ + uint32_t op; + + add_payload(r, offset, len); + + if (invflags & IPT_INV_SRCIP || invflags & IPT_INV_DSTIP) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_ptr(r, op, data, len); +} + +void add_proto(struct nft_rule *r, int offset, size_t len, + uint32_t proto, int invflags) +{ + uint32_t op; + + add_payload(r, offset, len); + + if (invflags & XT_INV_PROTO) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_u32(r, proto, op); +} + +bool is_same_interfaces(const char *a_iniface, const char *a_outiface, + unsigned const char *a_iniface_mask, + unsigned const char *a_outiface_mask, + const char *b_iniface, const char *b_outiface, + unsigned const char *b_iniface_mask, + unsigned const char *b_outiface_mask) +{ + int i; + + for (i = 0; i < IFNAMSIZ; i++) { + if (a_iniface_mask[i] != b_iniface_mask[i]) { + DEBUGP("different iniface mask %x, %x (%d)\n", + a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i); + return false; + } + if ((a_iniface[i] & a_iniface_mask[i]) + != (b_iniface[i] & b_iniface_mask[i])) { + DEBUGP("different iniface\n"); + return false; + } + if (a_outiface_mask[i] != b_outiface_mask[i]) { + DEBUGP("different outiface mask\n"); + return false; + } + if ((a_outiface[i] & a_outiface_mask[i]) + != (b_outiface[i] & b_outiface_mask[i])) { + DEBUGP("different outiface\n"); + return false; + } + } + + return true; +} + +void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, + unsigned char *iniface_mask, char *outiface, + unsigned char *outiface_mask, uint8_t *invflags) +{ + uint32_t value; + const void *ifname; + size_t len; + + switch(key) { + case NFT_META_IIF: + value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); + if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + *invflags |= IPT_INV_VIA_IN; + + if_indextoname(value, iniface); + + memset(iniface_mask, 0xff, strlen(iniface)+1); + break; + case NFT_META_OIF: + value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); + if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + *invflags |= IPT_INV_VIA_OUT; + + if_indextoname(value, outiface); + + memset(outiface_mask, 0xff, strlen(outiface)+1); + break; + case NFT_META_IIFNAME: + ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); + if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + *invflags |= IPT_INV_VIA_IN; + + memcpy(iniface, ifname, len); + iniface[len] = '\0'; + + /* If zero, then this is an interface mask */ + if (if_nametoindex(iniface) == 0) { + iniface[len] = '+'; + iniface[len+1] = '\0'; + } + + memset(iniface_mask, 0xff, len); + break; + case NFT_META_OIFNAME: + ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); + if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + *invflags |= IPT_INV_VIA_OUT; + + memcpy(outiface, ifname, len); + outiface[len] = '\0'; + + /* If zero, then this is an interface mask */ + if (if_nametoindex(outiface) == 0) { + outiface[len] = '+'; + outiface[len+1] = '\0'; + } + + memset(outiface_mask, 0xff, len); + break; + default: + DEBUGP("unknown meta key %d\n", key); + break; + } +} + +void print_proto(uint16_t proto, int invert) +{ + const struct protoent *pent = getprotobynumber(proto); + + if (invert) + printf("! "); + + if (pent) { + printf("-p %s ", pent->p_name); + return; + } + + printf("-p %u ", proto); +} + +void get_cmp_data(struct nft_rule_expr_iter *iter, + void *data, size_t dlen, bool *inv) +{ + struct nft_rule_expr *e; + const char *name; + size_t len; + uint8_t op; + + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + if (strcmp(name, "cmp") != 0) { + DEBUGP("skipping no cmp after meta\n"); + return; + } + + memcpy(data, nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len), dlen); + op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); + if (op == NFT_CMP_NEQ) + *inv = true; + else + *inv = false; +} + +void print_num(uint64_t number, unsigned int format) +{ + if (format & FMT_KILOMEGAGIGA) { + if (number > 99999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + printf(FMT("%4lluT ","%lluT "), (unsigned long long)number); + } + else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number); + } + else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number); + } else + printf(FMT("%4lluK ","%lluK "), (unsigned long long)number); + } else + printf(FMT("%5llu ","%llu "), (unsigned long long)number); + } else + printf(FMT("%8llu ","%llu "), (unsigned long long)number); +} + +void print_firewall_details(const struct iptables_command_state *cs, + const char *targname, uint8_t flags, + uint8_t invflags, uint8_t proto, + const char *iniface, const char *outiface, + unsigned int num, unsigned int format) +{ + if (format & FMT_LINENUMBERS) + printf(FMT("%-4u ", "%u "), num); + + if (!(format & FMT_NOCOUNTS)) { + print_num(cs->counters.pcnt, format); + print_num(cs->counters.bcnt, format); + } + + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ", "%s "), targname ? targname : ""); + + fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout); + { + const char *pname = + proto_to_name(proto, format&FMT_NUMERIC); + if (pname) + printf(FMT("%-5s", "%s "), pname); + else + printf(FMT("%-5hu", "%hu "), proto); + } + + if (format & FMT_OPTIONS) { + if (format & FMT_NOTABLE) + fputs("opt ", stdout); + fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); + fputc(' ', stdout); + } + + if (format & FMT_VIA) { + char iface[IFNAMSIZ+2]; + if (invflags & IPT_INV_VIA_IN) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (iniface[0] != '\0') { + strcat(iface, iniface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT(" %-6s ","in %s "), iface); + + if (invflags & IPT_INV_VIA_OUT) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (outiface[0] != '\0') { + strcat(iface, outiface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT("%-6s ","out %s "), iface); + } +} + +struct nft_family_ops *nft_family_ops_lookup(int family) +{ + switch (family) { + case AF_INET: + return &nft_family_ops_ipv4; + case AF_INET6: + return &nft_family_ops_ipv6; + default: + break; + } + + return NULL; +} + diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h new file mode 100644 index 00000000..4b2594fa --- /dev/null +++ b/iptables/nft-shared.h @@ -0,0 +1,110 @@ +/* + * Authors: + * (C) 2013 by Tomasz Bursztyka + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _NFT_SHARED_H_ +#define _NFT_SHARED_H_ + +#include + +#include +#include + +#include "xshared.h" + +#if 0 +#define DEBUGP(x, args...) fprintf(stdout, x, ## args) +#define NLDEBUG +#define DEBUG_DEL +#else +#define DEBUGP(x, args...) +#endif + +/* + * iptables print output emulation + */ + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + +struct nft_family_ops { + int (*add)(struct nft_rule *r, struct iptables_command_state *cs); + bool (*is_same)(const struct iptables_command_state *a, + const struct iptables_command_state *b); + void (*print_payload)(struct nft_rule_expr *e, + struct nft_rule_expr_iter *iter); + void (*parse_meta)(struct nft_rule_expr *e, uint8_t key, + struct iptables_command_state *cs); + void (*parse_payload)(struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs, + uint32_t offset); + void (*parse_immediate)(struct iptables_command_state *cs); + uint8_t (*print_firewall)(const struct iptables_command_state *cs, + const char *targname, unsigned int num, + unsigned int format); +}; + +void add_meta(struct nft_rule *r, uint32_t key); +void add_payload(struct nft_rule *r, int offset, int len); +void add_bitwise_u16(struct nft_rule *r, int mask, int xor); +void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len); +void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op); +void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op); +void add_iniface(struct nft_rule *r, char *iface, int invflags); +void add_outiface(struct nft_rule *r, char *iface, int invflags); +void add_addr(struct nft_rule *r, int offset, + void *data, size_t len, int invflags); +void add_proto(struct nft_rule *r, int offset, size_t len, + uint32_t proto, int invflags); + +bool is_same_interfaces(const char *a_iniface, const char *a_outiface, + unsigned const char *a_iniface_mask, + unsigned const char *a_outiface_mask, + const char *b_iniface, const char *b_outiface, + unsigned const char *b_iniface_mask, + unsigned const char *b_outiface_mask); + +void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, + unsigned char *iniface_mask, char *outiface, + unsigned char *outiface_mask, uint8_t *invflags); + +void print_proto(uint16_t proto, int invert); +void get_cmp_data(struct nft_rule_expr_iter *iter, + void *data, size_t dlen, bool *inv); +void print_num(uint64_t number, unsigned int format); +void print_firewall_details(const struct iptables_command_state *cs, + const char *targname, uint8_t flags, + uint8_t invflags, uint8_t proto, + const char *iniface, const char *outiface, + unsigned int num, unsigned int format); + +struct nft_family_ops *nft_family_ops_lookup(int family); + +#endif diff --git a/iptables/nft.c b/iptables/nft.c index 097c28b9..273d0838 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -9,14 +9,6 @@ * This code has been sponsored by Sophos Astaro */ -#if 0 -#define DEBUGP(x, args...) fprintf(stdout, x, ## args) -#define NLDEBUG -#define DEBUG_DEL -#else -#define DEBUGP(x, args...) -#endif - #include #include #include @@ -54,6 +46,7 @@ #include "nft.h" #include "xshared.h" /* proto_to_name */ +#include "nft-shared.h" static void *nft_fn; @@ -655,80 +648,6 @@ static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) #endif } -static void add_meta(struct nft_rule *r, uint32_t key) -{ - struct nft_rule_expr *expr; - - expr = nft_rule_expr_alloc("meta"); - if (expr == NULL) - return; - - nft_rule_expr_set_u32(expr, NFT_EXPR_META_KEY, key); - nft_rule_expr_set_u32(expr, NFT_EXPR_META_DREG, NFT_REG_1); - - nft_rule_add_expr(r, expr); -} - -static void add_payload(struct nft_rule *r, int offset, int len) -{ - struct nft_rule_expr *expr; - - expr = nft_rule_expr_alloc("payload"); - if (expr == NULL) - return; - - nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_BASE, - NFT_PAYLOAD_NETWORK_HEADER); - nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_DREG, NFT_REG_1); - nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_OFFSET, offset); - nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_LEN, len); - - nft_rule_add_expr(r, expr); -} - -/* bitwise operation is = sreg & mask ^ xor */ -static void add_bitwise_u16(struct nft_rule *r, int mask, int xor) -{ - struct nft_rule_expr *expr; - - expr = nft_rule_expr_alloc("bitwise"); - if (expr == NULL) - return; - - nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_SREG, NFT_REG_1); - nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_DREG, NFT_REG_1); - nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_LEN, sizeof(uint16_t)); - nft_rule_expr_set(expr, NFT_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t)); - nft_rule_expr_set(expr, NFT_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t)); - - nft_rule_add_expr(r, expr); -} - -static void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len) -{ - struct nft_rule_expr *expr; - - expr = nft_rule_expr_alloc("cmp"); - if (expr == NULL) - return; - - nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_SREG, NFT_REG_1); - nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_OP, op); - nft_rule_expr_set(expr, NFT_EXPR_CMP_DATA, data, len); - - nft_rule_add_expr(r, expr); -} - -static void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op) -{ - add_cmp_ptr(r, op, &val, sizeof(val)); -} - -static void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op) -{ - add_cmp_ptr(r, op, &val, sizeof(val)); -} - static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) { struct nft_rule_expr *expr; @@ -743,63 +662,6 @@ static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) nft_rule_add_expr(r, expr); } -static void add_iniface(struct nft_rule *r, char *iface, int invflags) -{ - int iface_len; - uint32_t op; - - iface_len = strlen(iface); - - if (invflags & IPT_INV_VIA_IN) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; - - if (iface[iface_len - 1] == '+') { - add_meta(r, NFT_META_IIFNAME); - add_cmp_ptr(r, op, iface, iface_len - 1); - } else { - add_meta(r, NFT_META_IIF); - add_cmp_u32(r, if_nametoindex(iface), op); - } -} - -static void add_outiface(struct nft_rule *r, char *iface, int invflags) -{ - int iface_len; - uint32_t op; - - iface_len = strlen(iface); - - if (invflags & IPT_INV_VIA_OUT) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; - - if (iface[iface_len - 1] == '+') { - add_meta(r, NFT_META_OIFNAME); - add_cmp_ptr(r, op, iface, iface_len - 1); - } else { - add_meta(r, NFT_META_OIF); - add_cmp_u32(r, if_nametoindex(iface), op); - } -} - -static void add_addr(struct nft_rule *r, int offset, - void *data, size_t len, int invflags) -{ - uint32_t op; - - add_payload(r, offset, len); - - if (invflags & IPT_INV_SRCIP || invflags & IPT_INV_DSTIP) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; - - add_cmp_ptr(r, op, data, len); -} - static void add_compat(struct nft_rule *r, uint32_t proto, bool inv) { nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_PROTO, proto); @@ -807,22 +669,6 @@ static void add_compat(struct nft_rule *r, uint32_t proto, bool inv) inv ? NFT_RULE_COMPAT_F_INV : 0); } -static void add_proto(struct nft_rule *r, int offset, size_t len, - uint32_t proto, int invflags) -{ - uint32_t op; - - add_payload(r, offset, len); - - if (invflags & XT_INV_PROTO) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; - - add_cmp_u32(r, proto, op); - add_compat(r, proto, invflags & XT_INV_PROTO); -} - int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs, @@ -833,7 +679,6 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct xtables_rule_match *matchp; struct nft_rule *r; int ret = 1; - uint32_t op; int flags = append ? NLM_F_APPEND : 0; int ip_flags = 0; @@ -851,73 +696,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); - switch (h->family) { - case AF_INET: - if (cs->fw.ip.iniface[0] != '\0') - add_iniface(r, cs->fw.ip.iniface, cs->fw.ip.invflags); - - if (cs->fw.ip.outiface[0] != '\0') - add_outiface(r, cs->fw.ip.outiface, - cs->fw.ip.invflags); - - if (cs->fw.ip.src.s_addr != 0) - add_addr(r, offsetof(struct iphdr, saddr), - &cs->fw.ip.src.s_addr, 4, - cs->fw.ip.invflags); - - if (cs->fw.ip.dst.s_addr != 0) - add_addr(r, offsetof(struct iphdr, daddr), - &cs->fw.ip.dst.s_addr, 4, - cs->fw.ip.invflags); - - if (cs->fw.ip.proto != 0) - add_proto(r, offsetof(struct iphdr, protocol), 1, - cs->fw.ip.proto, cs->fw.ip.invflags); - - if (cs->fw.ip.flags & IPT_F_FRAG) { - add_payload(r, offsetof(struct iphdr, frag_off), 2); - /* get the 13 bits that contain the fragment offset */ - add_bitwise_u16(r, 0x1fff, !0x1fff); - - /* if offset is non-zero, this is a fragment */ - if (cs->fw.ip.invflags & IPT_INV_FRAG) - op = NFT_CMP_EQ; - else - op = NFT_CMP_NEQ; - - add_cmp_u16(r, 0, op); - } - - ip_flags = cs->fw.ip.flags; - - break; - case AF_INET6: - if (cs->fw6.ipv6.iniface[0] != '\0') - add_iniface(r, cs->fw6.ipv6.iniface, - cs->fw6.ipv6.invflags); - - if (cs->fw6.ipv6.outiface[0] != '\0') - add_outiface(r, cs->fw6.ipv6.outiface, - cs->fw6.ipv6.invflags); - - if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)) - add_addr(r, offsetof(struct ip6_hdr, ip6_src), - &cs->fw6.ipv6.src, 16, - cs->fw6.ipv6.invflags); - - if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)) - add_addr(r, offsetof(struct ip6_hdr, ip6_dst), - &cs->fw6.ipv6.dst, 16, - cs->fw6.ipv6.invflags); - - if (cs->fw6.ipv6.proto != 0) - add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1, - cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags); - - ip_flags = cs->fw6.ipv6.flags; - - break; - } + ip_flags = h->ops->add(r, cs); for (matchp = cs->matches; matchp; matchp = matchp->next) add_match(r, matchp->match->m); @@ -1177,203 +956,6 @@ nft_print_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) } } -static void -get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv) -{ - struct nft_rule_expr *e; - const char *name; - size_t len; - uint8_t op; - - e = nft_rule_expr_iter_next(iter); - if (e == NULL) - return; - - name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); - if (strcmp(name, "cmp") != 0) { - DEBUGP("skipping no cmp after meta\n"); - return; - } - - memcpy(data, nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len), dlen); - op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); - if (op == NFT_CMP_NEQ) - *inv = true; - else - *inv = false; -} - -static void get_frag(struct nft_rule_expr_iter *iter, bool *inv) -{ - struct nft_rule_expr *e; - const char *name; - uint8_t op; - - e = nft_rule_expr_iter_next(iter); - if (e == NULL) - return; - - /* we assume correct mask and xor */ - name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); - if (strcmp(name, "bitwise") != 0) { - DEBUGP("skipping no bitwise after payload\n"); - return; - } - - /* Now check for cmp */ - e = nft_rule_expr_iter_next(iter); - if (e == NULL) - return; - - /* we assume correct data */ - name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); - if (strcmp(name, "cmp") != 0) { - DEBUGP("skipping no cmp after payload\n"); - return; - } - - op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); - if (op == NFT_CMP_EQ) - *inv = true; - else - *inv = false; -} - -static void print_frag(bool inv) -{ - if (inv) - printf("! -f "); - else - printf("-f "); -} - -static void print_proto(uint16_t proto, int invert) -{ - const struct protoent *pent = getprotobynumber(proto); - - if (invert) - printf("! "); - - if (pent) { - printf("-p %s ", pent->p_name); - return; - } - - printf("-p %u ", proto); -} - -static const char *mask_to_str(uint32_t mask) -{ - static char mask_str[sizeof("255.255.255.255")]; - uint32_t bits, hmask = ntohl(mask); - struct in_addr mask_addr = { - .s_addr = mask, - }; - int i; - - if (mask == 0xFFFFFFFFU) { - sprintf(mask_str, "32"); - return mask_str; - } - - i = 32; - bits = 0xFFFFFFFEU; - while (--i >= 0 && hmask != bits) - bits <<= 1; - if (i >= 0) - sprintf(mask_str, "%u", i); - else - sprintf(mask_str, "%s", inet_ntoa(mask_addr)); - - return mask_str; -} - -static void -nft_print_payload_ipv4(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) -{ - uint32_t offset; - bool inv; - - offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - - switch(offset) { - struct in_addr addr; - uint8_t proto; - - case offsetof(struct iphdr, saddr): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - if (inv) - printf("! -s %s/%s ", inet_ntoa(addr), - mask_to_str(0xffffffff)); - else - printf("-s %s/%s ", inet_ntoa(addr), - mask_to_str(0xffffffff)); - break; - case offsetof(struct iphdr, daddr): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - if (inv) - printf("! -d %s/%s ", inet_ntoa(addr), - mask_to_str(0xffffffff)); - else - printf("-d %s/%s ", inet_ntoa(addr), - mask_to_str(0xffffffff)); - break; - case offsetof(struct iphdr, protocol): - get_cmp_data(iter, &proto, sizeof(proto), &inv); - print_proto(proto, inv); - break; - case offsetof(struct iphdr, frag_off): - get_frag(iter, &inv); - print_frag(inv); - break; - default: - DEBUGP("unknown payload offset %d\n", offset); - break; - } -} - -static void -nft_print_payload_ipv6(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) -{ - uint32_t offset; - bool inv; - - offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - - switch (offset) { - char addr_str[INET6_ADDRSTRLEN]; - struct in6_addr addr; - uint8_t proto; - case offsetof(struct ip6_hdr, ip6_src): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); - - if (inv) - printf("! -s %s ", addr_str); - else - printf("-s %s ", addr_str); - - break; - case offsetof(struct ip6_hdr, ip6_dst): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); - - if (inv) - printf("! -d %s ", addr_str); - else - printf("-d %s ", addr_str); - - break; - case offsetof(struct ip6_hdr, ip6_nxt): - get_cmp_data(iter, &proto, sizeof(proto), &inv); - print_proto(proto, inv); - break; - default: - DEBUGP("unknown payload offset %d\n", offset); - break; - } -} - static void nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, bool counters) @@ -1414,10 +996,9 @@ nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters) if (strcmp(name, "counter") == 0) { nft_print_counters(expr, iter, counters); } else if (strcmp(name, "payload") == 0) { - if (nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY) == AF_INET) - nft_print_payload_ipv4(expr, iter); - else - nft_print_payload_ipv6(expr, iter); + struct nft_family_ops *ops = nft_family_ops_lookup( + nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)); + ops->print_payload(expr, iter); } else if (strcmp(name, "meta") == 0) { nft_print_meta(expr, iter); } else if (strcmp(name, "match") == 0) { @@ -2106,107 +1687,13 @@ match_different(const struct xt_entry_match *a, return 0; } -static bool -is_same(int family, const struct iptables_command_state *a, - const struct iptables_command_state *b) -{ - unsigned int i; - const char *a_outiface, *a_iniface; - unsigned const char *a_iniface_mask, *a_outiface_mask; - const char *b_outiface, *b_iniface; - unsigned const char *b_iniface_mask, *b_outiface_mask; - - /* Always compare head structures: ignore mask here. */ - switch (family) { - case AF_INET: - if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr - || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr - || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr - || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr - || a->fw.ip.proto != b->fw.ip.proto - || a->fw.ip.flags != b->fw.ip.flags - || a->fw.ip.invflags != b->fw.ip.invflags) { - DEBUGP("different src/dst/proto/flags/invflags\n"); - return false; - } - - a_iniface_mask = a->fw.ip.iniface_mask; - a_iniface = a->fw.ip.iniface; - a_outiface_mask = a->fw.ip.outiface_mask; - a_outiface = a->fw.ip.outiface; - - b_iniface_mask = b->fw.ip.iniface_mask; - b_iniface = b->fw.ip.iniface; - b_outiface_mask = b->fw.ip.outiface_mask; - b_outiface = b->fw.ip.outiface; - - break; - case AF_INET6: - if (memcmp(a->fw6.ipv6.src.s6_addr, - b->fw6.ipv6.src.s6_addr, - sizeof(struct in6_addr)) != 0 || - memcmp(a->fw6.ipv6.dst.s6_addr, - b->fw6.ipv6.dst.s6_addr, - sizeof(struct in6_addr)) != 0 || - a->fw6.ipv6.proto != b->fw6.ipv6.proto || - a->fw6.ipv6.flags != b->fw6.ipv6.flags || - a->fw6.ipv6.invflags != b->fw6.ipv6.invflags) { - DEBUGP("different src/dst/proto/flags/invflags\n"); - return false; - } - - a_iniface_mask = a->fw6.ipv6.iniface_mask; - a_iniface = a->fw6.ipv6.iniface; - a_outiface_mask = a->fw6.ipv6.outiface_mask; - a_outiface = a->fw6.ipv6.outiface; - - b_iniface_mask = b->fw6.ipv6.iniface_mask; - b_iniface = b->fw6.ipv6.iniface; - b_outiface_mask = b->fw6.ipv6.outiface_mask; - b_outiface = b->fw6.ipv6.outiface; - - break; - default: - return false; - } - - for (i = 0; i < IFNAMSIZ; i++) { - if (a_iniface_mask[i] != b_iniface_mask[i]) { - DEBUGP("different iniface mask %x, %x (%d)\n", - a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i); - return false; - } - if ((a_iniface[i] & a_iniface_mask[i]) - != (b_iniface[i] & b_iniface_mask[i])) { - DEBUGP("different iniface\n"); - return false; - } - if (a_outiface_mask[i] != b_outiface_mask[i]) { - DEBUGP("different outiface mask\n"); - return false; - } - if ((a_outiface[i] & a_outiface_mask[i]) - != (b_outiface[i] & b_outiface_mask[i])) { - DEBUGP("different outiface\n"); - return false; - } - } - - return true; -} - static void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, struct iptables_command_state *cs) { uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); - uint32_t value; + struct nft_family_ops *ops = nft_family_ops_lookup(family); const char *name; - const void *ifname; - char *iniface, *outiface; - unsigned char *iniface_mask, *outiface_mask; - size_t len; - uint8_t *invflags; e = nft_rule_expr_iter_next(iter); if (e == NULL) @@ -2218,173 +1705,19 @@ nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, return; } - switch (family) { - case AF_INET: - iniface = cs->fw.ip.iniface; - outiface = cs->fw.ip.outiface; - iniface_mask = cs->fw.ip.iniface_mask; - outiface_mask = cs->fw.ip.outiface_mask; - invflags = &cs->fw.ip.invflags; - break; - case AF_INET6: - iniface = cs->fw6.ipv6.iniface; - outiface = cs->fw6.ipv6.outiface; - iniface_mask = cs->fw6.ipv6.iniface_mask; - outiface_mask = cs->fw6.ipv6.outiface_mask; - invflags = &cs->fw6.ipv6.invflags; - break; - default: - return; - } - - switch(key) { - case NFT_META_IIF: - value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); - if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - *invflags |= IPT_INV_VIA_IN; - - if_indextoname(value, iniface); - - memset(iniface_mask, 0xff, strlen(iniface)+1); - break; - case NFT_META_OIF: - value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); - if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - *invflags |= IPT_INV_VIA_OUT; - - if_indextoname(value, outiface); - - memset(outiface_mask, 0xff, strlen(outiface)+1); - break; - case NFT_META_IIFNAME: - ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); - if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - *invflags |= IPT_INV_VIA_IN; - - memcpy(iniface, ifname, len); - iniface[len] = '\0'; - - /* If zero, then this is an interface mask */ - if (if_nametoindex(iniface) == 0) { - iniface[len] = '+'; - iniface[len+1] = '\0'; - } - - memset(iniface_mask, 0xff, len); - break; - case NFT_META_OIFNAME: - ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); - if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - *invflags |= IPT_INV_VIA_OUT; - - memcpy(outiface, ifname, len); - outiface[len] = '\0'; - - /* If zero, then this is an interface mask */ - if (if_nametoindex(outiface) == 0) { - outiface[len] = '+'; - outiface[len+1] = '\0'; - } - - memset(outiface_mask, 0xff, len); - break; - default: - DEBUGP("unknown meta key %d\n", key); - break; - } -} - -static void -nft_parse_payload_ipv4(uint32_t offset, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) -{ - switch(offset) { - struct in_addr addr; - uint8_t proto; - bool inv; - - case offsetof(struct iphdr, saddr): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - cs->fw.ip.src.s_addr = addr.s_addr; - cs->fw.ip.smsk.s_addr = 0xffffffff; - if (inv) - cs->fw.ip.invflags |= IPT_INV_SRCIP; - break; - case offsetof(struct iphdr, daddr): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - cs->fw.ip.dst.s_addr = addr.s_addr; - cs->fw.ip.dmsk.s_addr = 0xffffffff; - if (inv) - cs->fw.ip.invflags |= IPT_INV_DSTIP; - break; - case offsetof(struct iphdr, protocol): - get_cmp_data(iter, &proto, sizeof(proto), &inv); - cs->fw.ip.proto = proto; - if (inv) - cs->fw.ip.invflags |= IPT_INV_PROTO; - break; - case offsetof(struct iphdr, frag_off): - cs->fw.ip.flags |= IPT_F_FRAG; - get_frag(iter, &inv); - if (inv) - cs->fw.ip.invflags |= IPT_INV_FRAG; - break; - default: - DEBUGP("unknown payload offset %d\n", offset); - break; - } -} - -static void -nft_parse_payload_ipv6(uint32_t offset, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) -{ - switch (offset) { - struct in6_addr addr; - uint8_t proto; - bool inv; - - case offsetof(struct ip6_hdr, ip6_src): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr)); - if (inv) - cs->fw6.ipv6.invflags |= IPT_INV_SRCIP; - break; - case offsetof(struct ip6_hdr, ip6_dst): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr)); - if (inv) - cs->fw6.ipv6.invflags |= IPT_INV_DSTIP; - break; - case offsetof(struct ip6_hdr, ip6_nxt): - get_cmp_data(iter, &proto, sizeof(proto), &inv); - cs->fw6.ipv6.flags |= IP6T_F_PROTO; - cs->fw6.ipv6.proto = proto; - if (inv) - cs->fw6.ipv6.invflags |= IPT_INV_PROTO; - break; - default: - DEBUGP("unknown payload offset %d\n", offset); - break; - } + ops->parse_meta(e, key, cs); } static void nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, struct iptables_command_state *cs) { + struct nft_family_ops *ops = nft_family_ops_lookup(family); uint32_t offset; offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - switch (family) { - case AF_INET: - nft_parse_payload_ipv4(offset, iter, cs); - break; - case AF_INET6: - nft_parse_payload_ipv6(offset, iter, cs); - break; - } + ops->parse_payload(iter, cs, offset); } static void @@ -2401,6 +1734,7 @@ nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, { int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); + struct nft_family_ops *ops; /* Standard target? */ switch(verdict) { @@ -2414,10 +1748,8 @@ nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, cs->jumpto = "RETURN"; return; case NFT_GOTO: - if (family == AF_INET) - cs->fw.ip.flags |= IPT_F_GOTO; - else - cs->fw6.ipv6.flags |= IPT_F_GOTO; + ops = nft_family_ops_lookup(family); + ops->parse_immediate(cs); case NFT_JUMP: cs->jumpto = chain; return; @@ -2705,6 +2037,8 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE); const char *rule_chain = nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); + const struct nft_family_ops *ops = nft_family_ops_lookup( + nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)); struct iptables_command_state this = {}; if (strcmp(table, rule_table) != 0 || @@ -2728,7 +2062,7 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, nft_rule_to_iptables_command_state(r, &this); - if (!is_same(nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY), cs, &this)) + if (!ops->is_same(cs, &this)) goto next; if (!find_matches(cs->matches, r)) { @@ -2887,49 +2221,6 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, return ret; } -/* - * iptables print output emulation - */ - -#define FMT_NUMERIC 0x0001 -#define FMT_NOCOUNTS 0x0002 -#define FMT_KILOMEGAGIGA 0x0004 -#define FMT_OPTIONS 0x0008 -#define FMT_NOTABLE 0x0010 -#define FMT_NOTARGET 0x0020 -#define FMT_VIA 0x0040 -#define FMT_NONEWLINE 0x0080 -#define FMT_LINENUMBERS 0x0100 - -#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ - | FMT_NUMERIC | FMT_NOTABLE) -#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) - -static void -print_num(uint64_t number, unsigned int format) -{ - if (format & FMT_KILOMEGAGIGA) { - if (number > 99999) { - number = (number + 500) / 1000; - if (number > 9999) { - number = (number + 500) / 1000; - if (number > 9999) { - number = (number + 500) / 1000; - if (number > 9999) { - number = (number + 500) / 1000; - printf(FMT("%4lluT ","%lluT "), (unsigned long long)number); - } - else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number); - } - else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number); - } else - printf(FMT("%4lluK ","%lluK "), (unsigned long long)number); - } else - printf(FMT("%5llu ","%llu "), (unsigned long long)number); - } else - printf(FMT("%8llu ","%llu "), (unsigned long long)number); -} - static void print_header(unsigned int format, const char *chain, const char *pol, const struct xt_counters *counters, bool basechain, uint32_t refs) @@ -3002,73 +2293,6 @@ print_match(struct nft_rule_expr *expr, int numeric) free(m); } -static void -print_ipv4_addr(const struct iptables_command_state *cs, unsigned int format) -{ - char buf[BUFSIZ]; - - fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); - if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) - printf(FMT("%-19s ","%s "), "anywhere"); - else { - if (format & FMT_NUMERIC) - strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src)); - else - strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src)); - strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); - printf(FMT("%-19s ","%s "), buf); - } - - fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); - if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) - printf(FMT("%-19s ","-> %s"), "anywhere"); - else { - if (format & FMT_NUMERIC) - strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst)); - else - strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst)); - strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); - printf(FMT("%-19s ","-> %s"), buf); - } -} - -static void -print_ipv6_addr(const struct iptables_command_state *cs, unsigned int format) -{ - char buf[BUFSIZ]; - - fputc(cs->fw6.ipv6.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); - if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src) - && !(format & FMT_NUMERIC)) - printf(FMT("%-19s ","%s "), "anywhere"); - else { - if (format & FMT_NUMERIC) - strcpy(buf, - xtables_ip6addr_to_numeric(&cs->fw6.ipv6.src)); - else - strcpy(buf, - xtables_ip6addr_to_anyname(&cs->fw6.ipv6.src)); - strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.smsk)); - printf(FMT("%-19s ","%s "), buf); - } - - - fputc(cs->fw6.ipv6.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); - if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst) - && !(format & FMT_NUMERIC)) - printf(FMT("%-19s ","-> %s"), "anywhere"); - else { - if (format & FMT_NUMERIC) - strcpy(buf, - xtables_ip6addr_to_numeric(&cs->fw6.ipv6.dst)); - else - strcpy(buf, - xtables_ip6addr_to_anyname(&cs->fw6.ipv6.dst)); - strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.dmsk)); - printf(FMT("%-19s ","-> %s"), buf); - } -} - static void print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, unsigned int num, unsigned int format) @@ -3077,10 +2301,8 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, const char *targname = NULL; const void *targinfo = NULL; int family; + struct nft_family_ops *ops; uint8_t flags = 0; - uint8_t invflags = 0; - uint8_t proto = 0; - const char *iniface = NULL, *outiface = NULL; struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; struct xt_entry_target *t; @@ -3132,91 +2354,9 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, nft_rule_expr_iter_destroy(iter); family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); + ops = nft_family_ops_lookup(family); - switch (family) { - case AF_INET: - flags = cs->fw.ip.flags; - invflags = flags = cs->fw.ip.invflags; - proto = cs->fw.ip.proto; - iniface = cs->fw.ip.iniface; - outiface = cs->fw.ip.outiface; - break; - case AF_INET6: - flags = cs->fw6.ipv6.flags; - invflags = cs->fw6.ipv6.invflags; - proto = cs->fw6.ipv6.proto; - iniface = cs->fw6.ipv6.iniface; - outiface = cs->fw6.ipv6.outiface; - break; - } - - if (format & FMT_LINENUMBERS) - printf(FMT("%-4u ", "%u "), num); - - if (!(format & FMT_NOCOUNTS)) { - print_num(cs->counters.pcnt, format); - print_num(cs->counters.bcnt, format); - } - - if (!(format & FMT_NOTARGET)) - printf(FMT("%-9s ", "%s "), targname ? targname : ""); - - fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout); - { - const char *pname = - proto_to_name(proto, format&FMT_NUMERIC); - if (pname) - printf(FMT("%-5s", "%s "), pname); - else - printf(FMT("%-5hu", "%hu "), proto); - } - - if (format & FMT_OPTIONS) { - if (format & FMT_NOTABLE) - fputs("opt ", stdout); - fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); - fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); - fputc(' ', stdout); - } - - if (format & FMT_VIA) { - char iface[IFNAMSIZ+2]; - if (invflags & IPT_INV_VIA_IN) { - iface[0] = '!'; - iface[1] = '\0'; - } - else iface[0] = '\0'; - - if (iniface[0] != '\0') { - strcat(iface, iniface); - } - else if (format & FMT_NUMERIC) strcat(iface, "*"); - else strcat(iface, "any"); - printf(FMT(" %-6s ","in %s "), iface); - - if (invflags & IPT_INV_VIA_OUT) { - iface[0] = '!'; - iface[1] = '\0'; - } - else iface[0] = '\0'; - - if (outiface[0] != '\0') { - strcat(iface, outiface); - } - else if (format & FMT_NUMERIC) strcat(iface, "*"); - else strcat(iface, "any"); - printf(FMT("%-6s ","out %s "), iface); - } - - - switch (family) { - case AF_INET: - print_ipv4_addr(cs, format); - break; - case AF_INET6: - print_ipv6_addr(cs, format); - break; - } + flags = ops->print_firewall(cs, targname, num, format); if (format & FMT_NOTABLE) fputs(" ", stdout); diff --git a/iptables/nft.h b/iptables/nft.h index 3cffb777..d2a9b928 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -2,6 +2,7 @@ #define _NFT_H_ #include "xshared.h" +#include "nft-shared.h" struct nft_handle { int family; @@ -9,6 +10,7 @@ struct nft_handle { uint32_t portid; uint32_t seq; bool commit; + struct nft_family_ops *ops; }; int nft_init(struct nft_handle *h); diff --git a/iptables/xtables.c b/iptables/xtables.c index 328bc695..c3110558 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -40,6 +40,7 @@ #include #include #include "xshared.h" +#include "nft-shared.h" #include "nft.h" #ifndef TRUE @@ -49,21 +50,6 @@ #define FALSE 0 #endif -#define FMT_NUMERIC 0x0001 -#define FMT_NOCOUNTS 0x0002 -#define FMT_KILOMEGAGIGA 0x0004 -#define FMT_OPTIONS 0x0008 -#define FMT_NOTABLE 0x0010 -#define FMT_NOTARGET 0x0020 -#define FMT_VIA 0x0040 -#define FMT_NONEWLINE 0x0080 -#define FMT_LINENUMBERS 0x0100 - -#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ - | FMT_NUMERIC | FMT_NOTABLE) -#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) - - #define CMD_NONE 0x0000U #define CMD_INSERT 0x0001U #define CMD_DELETE 0x0002U @@ -1262,6 +1248,9 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) } h->family = family; + h->ops = nft_family_ops_lookup(family); + if (h->ops == NULL) + xtables_error(PARAMETER_PROBLEM, "Unknown family"); if (command == CMD_REPLACE && (s.naddrs != 1 || d.naddrs != 1)) xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " -- cgit v1.2.3 From 3f7877e6be987bb94897c03a45945725389a6f5c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 23 Feb 2013 18:27:08 +0100 Subject: xtables-restore: add -4 and -6 support Now you can specify: xtables-restore -6 < my-ip6tables-ruleset to restore the IPv6 rule-set. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 2 +- iptables/xtables-restore.c | 10 +++++++++- iptables/xtables.c | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 273d0838..88fd84be 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2630,7 +2630,7 @@ static int nft_action(struct nft_handle *h, int type) nlh->nlmsg_seq = seq = time(NULL); struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); - nfg->nfgen_family = AF_INET; + nfg->nfgen_family = h->family; nfg->version = NFNETLINK_V0; nfg->res_id = 0; diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index c62b0a9a..3b14a9f7 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -36,6 +36,8 @@ static const struct option options[] = { {.name = "noflush", .has_arg = false, .val = 'n'}, {.name = "modprobe", .has_arg = true, .val = 'M'}, {.name = "table", .has_arg = true, .val = 'T'}, + {.name = "ipv4", .has_arg = false, .val = '4'}, + {.name = "ipv6", .has_arg = false, .val = '6'}, {NULL}, }; @@ -193,7 +195,7 @@ xtables_restore_main(int argc, char *argv[]) nft_init(&h); - while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "bcvthnM:T:46", options, NULL)) != -1) { switch (c) { case 'b': binary = 1; @@ -220,6 +222,12 @@ xtables_restore_main(int argc, char *argv[]) case 'T': tablename = optarg; break; + case '4': + h.family = AF_INET; + break; + case '6': + h.family = AF_INET6; + break; } } diff --git a/iptables/xtables.c b/iptables/xtables.c index c3110558..c0a6347a 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -1247,7 +1247,10 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) break; } - h->family = family; + /* Set only if required, needed by xtables-restore */ + if (h->family == AF_UNSPEC) + h->family = family; + h->ops = nft_family_ops_lookup(family); if (h->ops == NULL) xtables_error(PARAMETER_PROBLEM, "Unknown family"); -- cgit v1.2.3 From 6fc6cb2d223da8d7fdf745cd783af8bcbc758b9f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 23 Feb 2013 18:30:36 +0100 Subject: xtables-save: add -4 and -6 support Now you can specify: xtables-save -6 > my-ip6tables-ruleset to dump the IPv6 rule-set. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-save.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 05d06b1e..111ad4be 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -32,6 +32,8 @@ static const struct option options[] = { {.name = "dump", .has_arg = false, .val = 'd'}, {.name = "table", .has_arg = true, .val = 't'}, {.name = "modprobe", .has_arg = true, .val = 'M'}, + {.name = "ipv4", .has_arg = false, .val = '4'}, + {.name = "ipv6", .has_arg = false, .val = '6'}, {NULL}, }; @@ -96,7 +98,7 @@ xtables_save_main(int argc, char *argv[]) #endif nft_init(&h); - while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "bcdt:46", options, NULL)) != -1) { switch (c) { case 'c': show_counters = true; @@ -112,6 +114,12 @@ xtables_save_main(int argc, char *argv[]) case 'd': do_output(&h, tablename, show_counters); exit(0); + case '4': + h.family = AF_INET; + break; + case '6': + h.family = AF_INET6; + break; } } -- cgit v1.2.3 From 45ce2daf44c11f53d208f607ccdd3d11192d0de5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 23 Feb 2013 19:36:46 +0100 Subject: nft: remove license for header file No tradition in the project to include the header file in .h file. This one is also internal - not exported. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.h | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 4b2594fa..f55d6f68 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -1,23 +1,3 @@ -/* - * Authors: - * (C) 2013 by Tomasz Bursztyka - * (C) 2012 by Pablo Neira Ayuso - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - #ifndef _NFT_SHARED_H_ #define _NFT_SHARED_H_ -- cgit v1.2.3 From 5231faea0fd5f5d4538a99d8234103a8297ff82f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 9 Mar 2013 01:01:11 +0100 Subject: xtables: fix missing xtables_exit_error definition iptables_exit_error is defined in iptables/iptables.c, that symbol cannot be used by iptables/xtables.c Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index c0a6347a..abc2379d 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -114,13 +114,13 @@ static struct option original_opts[] = { {NULL}, }; -void iptables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); +void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); struct xtables_globals xtables_globals = { .option_offset = 0, .program_version = IPTABLES_VERSION, .orig_opts = original_opts, - .exit_err = iptables_exit_error, + .exit_err = xtables_exit_error, .compat_rev = nft_compatible_revision, }; @@ -295,6 +295,26 @@ exit_printhelp(const struct xtables_rule_match *matches) exit(0); } +void +xtables_exit_error(enum xtables_exittype status, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", prog_name, prog_vers); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + /* On error paths, make sure that we don't leak memory */ + xtables_free_opts(1); + exit(status); +} + static void generic_opt_check(int command, int options) { -- cgit v1.2.3 From c1ee3f1849436d81579632a1cc8ba6a4b878fc3c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 9 Mar 2013 01:04:29 +0100 Subject: xtables-standalone: fix error message xtables -m tcp -h (nil) v1.4.15: ... ^^^^^ Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-standalone.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index 28416117..4299506a 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -48,12 +48,12 @@ xtables_main(int argc, char *argv[]) memset(&h, 0, sizeof(h)); - iptables_globals.program_name = "xtables"; + xtables_globals.program_name = "xtables"; ret = xtables_init_all(&xtables_globals, NFPROTO_IPV4); if (ret < 0) { fprintf(stderr, "%s/%s Failed to initialize xtables\n", - iptables_globals.program_name, - iptables_globals.program_version); + xtables_globals.program_name, + xtables_globals.program_version); exit(1); } #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) -- cgit v1.2.3 From c924c0cd07440aa9ce7465e2ba68fb266f07d7c3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 10 Mar 2013 11:43:32 +0100 Subject: xtables-config: priority has to be per-chain to support To support NAT table chain configuration appropriately. Modify example configuration file as well. Signed-off-by: Pablo Neira Ayuso --- etc/xtables.conf | 41 +++++++++++++++++++++++----------------- iptables/xtables-config-parser.y | 14 +++++++------- iptables/xtables-config.c | 4 +++- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/etc/xtables.conf b/etc/xtables.conf index 00b5df4f..6d26ffe4 100644 --- a/etc/xtables.conf +++ b/etc/xtables.conf @@ -1,24 +1,31 @@ -table raw prio -300 { - chain PREROUTING hook NF_INET_PRE_ROUTING - chain OUTPUT hook NF_INET_LOCAL_OUT +table raw { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -300 + chain OUTPUT hook NF_INET_LOCAL_OUT prio -300 } -table mangle prio -150 { - chain PREROUTING hook NF_INET_PRE_ROUTING - chain INPUT hook NF_INET_LOCAL_IN - chain FORWARD hook NF_INET_FORWARD - chain OUTPUT hook NF_INET_LOCAL_OUT - chain POSTROUTING hook NF_INET_POST_ROUTING +table mangle { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -150 + chain INPUT hook NF_INET_LOCAL_IN prio -150 + chain FORWARD hook NF_INET_FORWARD prio -150 + chain OUTPUT hook NF_INET_LOCAL_OUT prio -150 + chain POSTROUTING hook NF_INET_POST_ROUTING prio -150 } -table filter prio 0 { - chain INPUT hook NF_INET_LOCAL_IN - chain FORWARD hook NF_INET_FORWARD - chain OUTPUT hook NF_INET_LOCAL_OUT +table filter { + chain INPUT hook NF_INET_LOCAL_IN prio 0 + chain FORWARD hook NF_INET_FORWARD prio 0 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 0 } -table security prio 150 { - chain INPUT hook NF_INET_LOCAL_IN - chain FORWARD hook NF_INET_FORWARD - chain OUTPUT hook NF_INET_LOCAL_OUT +table nat { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -100 + chain POSTROUTING hook NF_INET_POST_ROUTING prio 100 + chain INPUT hook NF_INET_LOCAL_IN prio -100 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 100 +} + +table security { + chain INPUT hook NF_INET_LOCAL_IN prio 150 + chain FORWARD hook NF_INET_FORWARD prio 150 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 150 } diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y index fe5bcbf1..ad5d624b 100644 --- a/iptables/xtables-config-parser.y +++ b/iptables/xtables-config-parser.y @@ -105,12 +105,10 @@ lines : line line : table ; -table : T_TABLE T_STRING T_PRIO T_INTEGER '{' chains '}' +table : T_TABLE T_STRING '{' chains '}' { /* added in reverse order to pop it in order */ - void *data = stack_push(T_PRIO, sizeof(int32_t)); - stack_put_i32(data, $4); - data = stack_push(T_TABLE, strlen($2)); + void *data = stack_push(T_TABLE, strlen($2)); stack_put_str(data, $2); } ; @@ -119,10 +117,12 @@ chains : chain | chains chain ; -chain : T_CHAIN T_STRING T_HOOK T_STRING +chain : T_CHAIN T_STRING T_HOOK T_STRING T_PRIO T_INTEGER { /* added in reverse order to pop it in order */ - void *data = stack_push(T_HOOK, strlen($4)); + void *data = stack_push(T_PRIO, sizeof(int32_t)); + stack_put_i32(data, $6); + data = stack_push(T_HOOK, strlen($4)); stack_put_str(data, $4); data = stack_push(T_CHAIN, strlen($2)); stack_put_str(data, $2); @@ -194,13 +194,13 @@ int xtables_config_parse(char *filename, struct nft_table_list *table_list, } nft_chain_attr_set(chain, NFT_CHAIN_ATTR_TABLE, (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); + nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio); nft_chain_attr_set(chain, NFT_CHAIN_ATTR_NAME, e->data); nft_chain_list_add(chain, chain_list); break; case T_HOOK: nft_chain_attr_set_u32(chain, NFT_CHAIN_ATTR_HOOKNUM, hooknametonum(e->data)); - nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio); break; default: printf("unknown token type %d\n", e->token); diff --git a/iptables/xtables-config.c b/iptables/xtables-config.c index 16918bf6..fce03a19 100644 --- a/iptables/xtables-config.c +++ b/iptables/xtables-config.c @@ -39,7 +39,9 @@ int xtables_config_main(int argc, char *argv[]) struct nft_table *table; struct nft_chain *chain; const char *filename = NULL; - struct nft_handle h; + struct nft_handle h = { + .family = AF_INET, + }; if (argc > 2) { fprintf(stderr, "Usage: %s []\n", argv[0]); -- cgit v1.2.3 From 8b9ea2e3f8d685a6b940691cabf5e82c96254747 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 10 Mar 2013 16:04:39 +0100 Subject: nft: load tables and chains based on /etc/xtables.conf If /etc/xtables.conf is available, use the configuration there to autoload the xtables built-in table and chain so you can define custom configurations. Otherwise, rely on default common table/chain configuration. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 96 +++++++++++++++++++++++++++++++++++++++++++++-- iptables/nft.h | 16 ++++++++ iptables/xtables-config.c | 75 ++---------------------------------- 3 files changed, 112 insertions(+), 75 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 88fd84be..f39f4071 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -17,6 +17,7 @@ #include #include /* getprotobynumber */ #include +#include #include #include @@ -47,6 +48,7 @@ #include "nft.h" #include "xshared.h" /* proto_to_name */ #include "nft-shared.h" +#include "xtables-config-parser.h" static void *nft_fn; @@ -683,7 +685,8 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, int ip_flags = 0; /* If built-in chains don't exist for this table, create them */ - nft_chain_builtin_init(h, table, chain, NF_ACCEPT); + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) + nft_chain_builtin_init(h, table, chain, NF_ACCEPT); nft_fn = nft_rule_add; @@ -1302,7 +1305,8 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl int ret; /* If built-in chains don't exist for this table, create them */ - nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) + nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); c = nft_chain_alloc(); if (c == NULL) { @@ -1469,7 +1473,8 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, int ret; /* If built-in chains don't exist for this table, create them */ - nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) + nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); /* Find the old chain to be renamed */ c = nft_chain_find(h, table, chain); @@ -2760,3 +2765,88 @@ const char *nft_strerror(int err) return strerror(err); } + +static void xtables_config_perror(uint32_t flags, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (flags & NFT_LOAD_VERBOSE) + vfprintf(stderr, fmt, args); + + va_end(args); +} + +int nft_xtables_config_load(struct nft_handle *h, const char *filename, + uint32_t flags) +{ + struct nft_table_list *table_list = nft_table_list_alloc(); + struct nft_chain_list *chain_list = nft_chain_list_alloc(); + struct nft_table_list_iter *titer; + struct nft_chain_list_iter *citer; + struct nft_table *table; + struct nft_chain *chain; + + if (xtables_config_parse(filename, table_list, chain_list) < 0) { + if (errno == ENOENT) { + xtables_config_perror(flags, + "configuration file `%s' does not exists\n", + filename); + } else { + xtables_config_perror(flags, + "Fatal error parsing config file: %s\n", + strerror(errno)); + } + return -1; + } + + nft_init(h); + + /* Stage 1) create tables */ + titer = nft_table_list_iter_create(table_list); + while ((table = nft_table_list_iter_next(titer)) != NULL) { + if (nft_table_add(h, table) < 0) { + if (errno == EEXIST) { + xtables_config_perror(flags, + "table `%s' already exists, skipping\n", + (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); + } else { + xtables_config_perror(flags, + "table `%s' cannot be create, reason `%s'. Exitting\n", + (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME), + strerror(errno)); + return -1; + } + continue; + } + xtables_config_perror(flags, "table `%s' has been created\n", + (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); + } + + /* Stage 2) create chains */ + citer = nft_chain_list_iter_create(chain_list); + while ((chain = nft_chain_list_iter_next(citer)) != NULL) { + if (nft_chain_add(h, chain) < 0) { + if (errno == EEXIST) { + xtables_config_perror(flags, + "chain `%s' already exists in table `%s', skipping\n", + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE)); + } else { + xtables_config_perror(flags, + "chain `%s' cannot be create, reason `%s'. Exitting\n", + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), + strerror(errno)); + return -1; + } + continue; + } + + xtables_config_perror(flags, + "chain `%s' in table `%s' has been created\n", + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), + (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE)); + } + return 0; +} diff --git a/iptables/nft.h b/iptables/nft.h index d2a9b928..8d5881d6 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -84,4 +84,20 @@ const char *nft_strerror(int err); /* For xtables.c */ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table); +/* + * Parse config for tables and chain helper functions + */ +#define XTABLES_CONFIG_DEFAULT "/etc/xtables.conf" + +struct nft_table_list; +struct nft_chain_list; + +extern int xtables_config_parse(const char *filename, struct nft_table_list *table_list, struct nft_chain_list *chain_list); + +enum { + NFT_LOAD_VERBOSE = (1 << 0), +}; + +int nft_xtables_config_load(struct nft_handle *h, const char *filename, uint32_t flags); + #endif diff --git a/iptables/xtables-config.c b/iptables/xtables-config.c index fce03a19..3ad46e85 100644 --- a/iptables/xtables-config.c +++ b/iptables/xtables-config.c @@ -16,32 +16,15 @@ #include #include -#include -#include - #include "xtables-multi.h" -#include "xtables-config-parser.h" - #include "nft.h" -extern int xtables_config_parse(const char *filename, - struct nft_table_list *table_list, - struct nft_chain_list *chain_list); - -#define XTABLES_CONFIG_DEFAULT "/etc/xtables.conf" - int xtables_config_main(int argc, char *argv[]) { - struct nft_table_list *table_list = nft_table_list_alloc(); - struct nft_chain_list *chain_list = nft_chain_list_alloc(); - struct nft_table_list_iter *titer; - struct nft_chain_list_iter *citer; - struct nft_table *table; - struct nft_chain *chain; - const char *filename = NULL; struct nft_handle h = { .family = AF_INET, }; + const char *filename = NULL; if (argc > 2) { fprintf(stderr, "Usage: %s []\n", argv[0]); @@ -52,58 +35,6 @@ int xtables_config_main(int argc, char *argv[]) else filename = argv[1]; - if (xtables_config_parse(filename, table_list, chain_list) < 0) { - if (errno == ENOENT) { - fprintf(stderr, "configuration file `%s' does not " - "exists\n", filename); - } else { - fprintf(stderr, "Fatal error: %s\n", strerror(errno)); - } - return EXIT_FAILURE; - } - - nft_init(&h); - - /* Stage 1) create tables */ - titer = nft_table_list_iter_create(table_list); - while ((table = nft_table_list_iter_next(titer)) != NULL) { - if (nft_table_add(&h, table) < 0) { - if (errno == EEXIST) { - printf("table `%s' already exists, skipping\n", - (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); - } else { - printf("table `%s' cannot be create, reason `%s'. Exitting\n", - (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME), - strerror(errno)); - return EXIT_FAILURE; - } - continue; - } - printf("table `%s' has been created\n", - (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); - } - - /* Stage 2) create chains */ - citer = nft_chain_list_iter_create(chain_list); - while ((chain = nft_chain_list_iter_next(citer)) != NULL) { - if (nft_chain_add(&h, chain) < 0) { - if (errno == EEXIST) { - printf("chain `%s' already exists in table `%s', skipping\n", - (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), - (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE)); - } else { - printf("chain `%s' cannot be create, reason `%s'. Exitting\n", - (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), - strerror(errno)); - return EXIT_FAILURE; - } - continue; - } - - printf("chain `%s' in table `%s' has been created\n", - (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), - (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE)); - } - - return EXIT_SUCCESS; + return nft_xtables_config_load(&h, filename, NFT_LOAD_VERBOSE) == 0 ? + EXIT_SUCCESS : EXIT_FAILURE; } -- cgit v1.2.3 From 32c579fa6fa1155f316c202a95d3e946111891bd Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 10 Mar 2013 16:20:27 +0100 Subject: xtables: support family in /etc/xtables.conf file Signed-off-by: Pablo Neira Ayuso --- etc/xtables.conf | 54 +++++++++++++++++++++------------------- iptables/xtables-config-parser.y | 32 +++++++++++++++++++++++- iptables/xtables-config-syntax.l | 1 + 3 files changed, 60 insertions(+), 27 deletions(-) diff --git a/etc/xtables.conf b/etc/xtables.conf index 6d26ffe4..1995b69f 100644 --- a/etc/xtables.conf +++ b/etc/xtables.conf @@ -1,31 +1,33 @@ -table raw { - chain PREROUTING hook NF_INET_PRE_ROUTING prio -300 - chain OUTPUT hook NF_INET_LOCAL_OUT prio -300 -} +family ipv4 { + table raw { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -300 + chain OUTPUT hook NF_INET_LOCAL_OUT prio -300 + } -table mangle { - chain PREROUTING hook NF_INET_PRE_ROUTING prio -150 - chain INPUT hook NF_INET_LOCAL_IN prio -150 - chain FORWARD hook NF_INET_FORWARD prio -150 - chain OUTPUT hook NF_INET_LOCAL_OUT prio -150 - chain POSTROUTING hook NF_INET_POST_ROUTING prio -150 -} + table mangle { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -150 + chain INPUT hook NF_INET_LOCAL_IN prio -150 + chain FORWARD hook NF_INET_FORWARD prio -150 + chain OUTPUT hook NF_INET_LOCAL_OUT prio -150 + chain POSTROUTING hook NF_INET_POST_ROUTING prio -150 + } -table filter { - chain INPUT hook NF_INET_LOCAL_IN prio 0 - chain FORWARD hook NF_INET_FORWARD prio 0 - chain OUTPUT hook NF_INET_LOCAL_OUT prio 0 -} + table filter { + chain INPUT hook NF_INET_LOCAL_IN prio 0 + chain FORWARD hook NF_INET_FORWARD prio 0 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 0 + } -table nat { - chain PREROUTING hook NF_INET_PRE_ROUTING prio -100 - chain POSTROUTING hook NF_INET_POST_ROUTING prio 100 - chain INPUT hook NF_INET_LOCAL_IN prio -100 - chain OUTPUT hook NF_INET_LOCAL_OUT prio 100 -} + table nat { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -100 + chain POSTROUTING hook NF_INET_POST_ROUTING prio 100 + chain INPUT hook NF_INET_LOCAL_IN prio -100 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 100 + } -table security { - chain INPUT hook NF_INET_LOCAL_IN prio 150 - chain FORWARD hook NF_INET_FORWARD prio 150 - chain OUTPUT hook NF_INET_LOCAL_OUT prio 150 + table security { + chain INPUT hook NF_INET_LOCAL_IN prio 150 + chain FORWARD hook NF_INET_FORWARD prio 150 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 150 + } } diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y index ad5d624b..06b6ca9f 100644 --- a/iptables/xtables-config-parser.y +++ b/iptables/xtables-config-parser.y @@ -84,6 +84,7 @@ static void stack_free(struct stack_elem *e) char *string; } +%token T_FAMILY %token T_TABLE %token T_CHAIN %token T_HOOK @@ -102,7 +103,18 @@ lines : line | lines line ; -line : table +line : family + ; + +family : T_FAMILY T_STRING '{' tables '}' + { + void *data = stack_push(T_FAMILY, strlen($2)); + stack_put_str(data, $2); + } + ; + +tables : table + | tables table ; table : T_TABLE T_STRING '{' chains '}' @@ -155,6 +167,16 @@ static int hooknametonum(const char *hookname) return -1; } +static int32_t familytonumber(const char *family) +{ + if (strcmp(family, "ipv4") == 0) + return AF_INET; + else if (strcmp(family, "ipv6") == 0) + return AF_INET6; + + return -1; +} + int xtables_config_parse(char *filename, struct nft_table_list *table_list, struct nft_chain_list *chain_list) { @@ -163,6 +185,7 @@ int xtables_config_parse(char *filename, struct nft_table_list *table_list, struct nft_table *table = NULL; struct nft_chain *chain = NULL; int prio = 0; + int32_t family = 0; fp = fopen(filename, "r"); if (!fp) @@ -174,12 +197,18 @@ int xtables_config_parse(char *filename, struct nft_table_list *table_list, for (e = stack_pop(); e != NULL; e = stack_pop()) { switch(e->token) { + case T_FAMILY: + family = familytonumber(e->data); + if (family == -1) + return -1; + break; case T_TABLE: table = nft_table_alloc(); if (table == NULL) { perror("nft_table_alloc"); return -1; } + nft_table_attr_set_u32(table, NFT_TABLE_ATTR_FAMILY, family); nft_table_attr_set(table, NFT_TABLE_ATTR_NAME, e->data); nft_table_list_add(table, table_list); break; @@ -194,6 +223,7 @@ int xtables_config_parse(char *filename, struct nft_table_list *table_list, } nft_chain_attr_set(chain, NFT_CHAIN_ATTR_TABLE, (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); + nft_table_attr_set_u32(table, NFT_CHAIN_ATTR_FAMILY, family); nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio); nft_chain_attr_set(chain, NFT_CHAIN_ATTR_NAME, e->data); nft_chain_list_add(chain, chain_list); diff --git a/iptables/xtables-config-syntax.l b/iptables/xtables-config-syntax.l index 7a66ef39..a895c8bc 100644 --- a/iptables/xtables-config-syntax.l +++ b/iptables/xtables-config-syntax.l @@ -28,6 +28,7 @@ integer [\-\+]?[0-9]+ string [a-zA-Z][a-zA-Z0-9\.\-\_]* %% +"family" { return T_FAMILY; } "table" { return T_TABLE; } "chain" { return T_CHAIN; } "hook" { return T_HOOK; } -- cgit v1.2.3 From 275283f176152a84212a37bb043d610077c7435b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 10 Mar 2013 16:56:20 +0100 Subject: xtables-config: fix off by one in parsed strings from /etc/xtables.conf Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-config-parser.y | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y index 06b6ca9f..6017e290 100644 --- a/iptables/xtables-config-parser.y +++ b/iptables/xtables-config-parser.y @@ -69,7 +69,7 @@ static inline void stack_put_i32(void *data, int value) static inline void stack_put_str(void *data, const char *str) { - memcpy(data, str, strlen(str)+1); + memcpy(data, str, strlen(str)); } static void stack_free(struct stack_elem *e) @@ -108,7 +108,7 @@ line : family family : T_FAMILY T_STRING '{' tables '}' { - void *data = stack_push(T_FAMILY, strlen($2)); + void *data = stack_push(T_FAMILY, strlen($2)+1); stack_put_str(data, $2); } ; @@ -120,7 +120,7 @@ tables : table table : T_TABLE T_STRING '{' chains '}' { /* added in reverse order to pop it in order */ - void *data = stack_push(T_TABLE, strlen($2)); + void *data = stack_push(T_TABLE, strlen($2)+1); stack_put_str(data, $2); } ; @@ -134,9 +134,9 @@ chain : T_CHAIN T_STRING T_HOOK T_STRING T_PRIO T_INTEGER /* added in reverse order to pop it in order */ void *data = stack_push(T_PRIO, sizeof(int32_t)); stack_put_i32(data, $6); - data = stack_push(T_HOOK, strlen($4)); + data = stack_push(T_HOOK, strlen($4)+1); stack_put_str(data, $4); - data = stack_push(T_CHAIN, strlen($2)); + data = stack_push(T_CHAIN, strlen($2)+1); stack_put_str(data, $2); } ; -- cgit v1.2.3 From 98ffe9f8a5aa730fed0a5b2f2ac337ca2511700e Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Wed, 6 Mar 2013 05:00:31 +0000 Subject: iptables: fix compilation when lib[mnl|nftables] are not in standard path There was several problems: a typo in the configure option a typo in CFLAGS addon for libmnl and libnftables paths to lib were missing on link Signed-off-by: Nicolas Dichtel Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- iptables/Makefile.am | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index eb2c367b..cbbf66df 100644 --- a/configure.ac +++ b/configure.ac @@ -123,7 +123,7 @@ PKG_CHECK_MODULES([libmnl], [libmnl >= 1.0], [mnl=1], [mnl=0]) AM_CONDITIONAL([HAVE_LIBMNL], [test "$mnl" = 1]) -PKG_CHECK_MODULES([libnfables], [libnftables >= 1.0], +PKG_CHECK_MODULES([libnftables], [libnftables >= 1.0], [nftables=1], [nftables=0]) AM_CONDITIONAL([HAVE_LIBNFTABLES], [test "$nftables" = 1]) diff --git a/iptables/Makefile.am b/iptables/Makefile.am index 11c501b9..5d653545 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -1,7 +1,7 @@ # -*- Makefile -*- AM_CFLAGS = ${regular_CFLAGS} -AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS} ${libmnl_CPPFLAGS} ${libnftables_CPPFLAGS} +AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS} ${libmnl_CFLAGS} ${libnftables_CFLAGS} AM_YFLAGS = -d xtables_multi_SOURCES = xtables-multi.c iptables-xml.c @@ -33,7 +33,7 @@ xtables_multi_SOURCES += xtables-save.c xtables-restore.c \ nft-shared.c nft-ipv4.c nft-ipv6.c \ xtables-config-parser.y xtables-config-syntax.l \ xtables-config.c xtables-events.c -xtables_multi_LDADD += -lmnl -lnftables +xtables_multi_LDADD += -lmnl -lnftables ${libmnl_LIBS} ${libnftables_LIBS} xtables_multi_CFLAGS += -DENABLE_NFTABLES # yacc and lex generate dirty code xtables_multi-xtables-config-parser.o xtables_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls -- cgit v1.2.3 From 4ef77b6d1b52e1fe52a7fd48d38d9233f0961640 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 24 Mar 2013 13:15:23 +0100 Subject: xtables: fix missing protocol and invflags xtables -I INPUT -p tcp --dport 22 -j ACCEPT iptables: Target problem. Run `dmesg' for more information x_tables: ip_tables: tcp match: only valid for protocol Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 2 ++ iptables/nft-ipv6.c | 2 ++ iptables/nft-shared.h | 1 + iptables/nft.c | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index ac2b27b8..0a601243 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -60,6 +60,8 @@ static int nft_ipv4_add(struct nft_rule *r, struct iptables_command_state *cs) add_cmp_u16(r, 0, op); } + add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags); + return cs->fw.ip.flags; } diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index a2e80306..65a89490 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -42,6 +42,8 @@ static int nft_ipv6_add(struct nft_rule *r, struct iptables_command_state *cs) add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags); + add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags); + return cs->fw6.ipv6.flags; } diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index f55d6f68..b301d411 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -63,6 +63,7 @@ void add_addr(struct nft_rule *r, int offset, void *data, size_t len, int invflags); void add_proto(struct nft_rule *r, int offset, size_t len, uint32_t proto, int invflags); +void add_compat(struct nft_rule *r, uint32_t proto, bool inv); bool is_same_interfaces(const char *a_iniface, const char *a_outiface, unsigned const char *a_iniface_mask, diff --git a/iptables/nft.c b/iptables/nft.c index f39f4071..e55c18c1 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -664,7 +664,7 @@ static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) nft_rule_add_expr(r, expr); } -static void add_compat(struct nft_rule *r, uint32_t proto, bool inv) +void add_compat(struct nft_rule *r, uint32_t proto, bool inv) { nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_PROTO, proto); nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_FLAGS, -- cgit v1.2.3 From 5c3cff616084aac880a16a31688e306e915f853c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 22 Apr 2013 13:43:07 +0200 Subject: xtables-config-parser: fix compilation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xtables-config-parser.y: In function ‘xtables_config_parse’: xtables-config-parser.y:216:4: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-config-parser.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y index 6017e290..1d78c43c 100644 --- a/iptables/xtables-config-parser.y +++ b/iptables/xtables-config-parser.y @@ -213,7 +213,7 @@ int xtables_config_parse(char *filename, struct nft_table_list *table_list, nft_table_list_add(table, table_list); break; case T_PRIO: - prio = *((int32_t *)e->data); + memcpy(&prio, e->data, sizeof(int32_t)); break; case T_CHAIN: chain = nft_chain_alloc(); -- cgit v1.2.3 From 4e7e5e838ce1d48737f4ae291d06f4f4c3708a16 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 22 Apr 2013 13:48:00 +0200 Subject: iptables: update .gitignore Ignore xtables-config-* generated by flex and bison. Signed-off-by: Pablo Neira Ayuso --- iptables/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/iptables/.gitignore b/iptables/.gitignore index 31baf7d7..6c0ade1a 100644 --- a/iptables/.gitignore +++ b/iptables/.gitignore @@ -11,5 +11,8 @@ /iptables-static /iptables-xml /xtables-multi +/xtables-config-parser.c +/xtables-config-parser.h +/xtables-config-syntax.c /xtables.pc -- cgit v1.2.3 From 26d3a0d77c67289341361bbd3254f2257eec69a0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 12 May 2013 15:33:54 +0200 Subject: xtables: add new container xtables_args structure Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.h | 28 ++++++ iptables/xtables.c | 253 +++++++++++++++++++++++--------------------------- 2 files changed, 145 insertions(+), 136 deletions(-) diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index b301d411..30e87bb3 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -88,4 +88,32 @@ void print_firewall_details(const struct iptables_command_state *cs, struct nft_family_ops *nft_family_ops_lookup(int family); +struct addr_mask { + union { + struct in_addr *v4; + struct in6_addr *v6; + } addr; + + unsigned int naddrs; + + union { + struct in_addr *v4; + struct in6_addr *v6; + } mask; +}; + +struct xtables_args { + int family; + uint16_t proto; + uint8_t flags; + uint8_t invflags; + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + bool goto_set; + const char *shostnetworkmask, *dhostnetworkmask; + const char *pcnt, *bcnt; + struct addr_mask s, d; + unsigned long long pcnt_cnt, bcnt_cnt; +}; + #endif diff --git a/iptables/xtables.c b/iptables/xtables.c index abc2379d..6a1a1646 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -190,20 +190,6 @@ enum { IPT_DOTTED_MASK }; -struct addr_mask { - union { - struct in_addr *v4; - struct in6_addr *v6; - } addr; - - unsigned int naddrs; - - union { - struct in_addr *v4; - struct in6_addr *v6; - } mask; -}; - static void __attribute__((noreturn)) exit_tryhelp(int status) { @@ -752,34 +738,17 @@ static void command_match(struct iptables_command_state *cs) int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) { struct iptables_command_state cs; - struct addr_mask s; - struct addr_mask d; - int verbose = 0; const char *chain = NULL; - const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; const char *policy = NULL, *newname = NULL; unsigned int rulenum = 0, command = 0; - const char *pcnt = NULL, *bcnt = NULL; int ret = 1; struct xtables_match *m; struct xtables_rule_match *matchp; struct xtables_target *t; - unsigned long long pcnt_cnt = 0, bcnt_cnt = 0; - - int family = AF_INET; - u_int16_t proto = 0; - u_int8_t flags = 0, invflags = 0; - char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; - unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; - bool goto_set = false; - - memset(&s, 0, sizeof(s)); - memset(&d, 0, sizeof(d)); - memset(iniface, 0, sizeof(iniface)); - memset(outiface, 0, sizeof(outiface)); - memset(iniface_mask, 0, sizeof(iniface_mask)); - memset(outiface_mask, 0, sizeof(outiface_mask)); + struct xtables_args args = { + .family = AF_INET, + }; memset(&cs, 0, sizeof(cs)); cs.jumpto = ""; @@ -969,7 +938,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) * Option selection */ case 'p': - set_option(&cs.options, OPT_PROTOCOL, &invflags, + set_option(&cs.options, OPT_PROTOCOL, &args.invflags, cs.invert); /* Canonicalize into lower case */ @@ -977,30 +946,30 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) *cs.protocol = tolower(*cs.protocol); cs.protocol = optarg; - proto = xtables_parse_protocol(cs.protocol); + args.proto = xtables_parse_protocol(cs.protocol); - if (proto == 0 && (invflags & XT_INV_PROTO)) + if (args.proto == 0 && (args.invflags & XT_INV_PROTO)) xtables_error(PARAMETER_PROBLEM, "rule would never match protocol"); break; case 's': - set_option(&cs.options, OPT_SOURCE, &invflags, + set_option(&cs.options, OPT_SOURCE, &args.invflags, cs.invert); - shostnetworkmask = optarg; + args.shostnetworkmask = optarg; break; case 'd': - set_option(&cs.options, OPT_DESTINATION, &invflags, - cs.invert); - dhostnetworkmask = optarg; + set_option(&cs.options, OPT_DESTINATION, + &args.invflags, cs.invert); + args.dhostnetworkmask = optarg; break; #ifdef IPT_F_GOTO case 'g': - set_option(&cs.options, OPT_JUMP, &invflags, + set_option(&cs.options, OPT_JUMP, &args.invflags, cs.invert); - goto_set = true; + args.goto_set = true; cs.jumpto = parse_target(optarg); break; #endif @@ -1015,11 +984,11 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "Empty interface is likely to be " "undesired"); - set_option(&cs.options, OPT_VIANAMEIN, &invflags, + set_option(&cs.options, OPT_VIANAMEIN, &args.invflags, cs.invert); xtables_parse_interface(optarg, - iniface, - iniface_mask); + args.iniface, + args.iniface_mask); break; case 'o': @@ -1027,28 +996,28 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "Empty interface is likely to be " "undesired"); - set_option(&cs.options, OPT_VIANAMEOUT, &invflags, + set_option(&cs.options, OPT_VIANAMEOUT, &args.invflags, cs.invert); xtables_parse_interface(optarg, - outiface, - outiface_mask); + args.outiface, + args.outiface_mask); break; case 'f': - if (family == AF_INET6) { + if (args.family == AF_INET6) { xtables_error(PARAMETER_PROBLEM, "`-f' is not supported in IPv6, " "use -m frag instead"); } - set_option(&cs.options, OPT_FRAGMENT, &invflags, + set_option(&cs.options, OPT_FRAGMENT, &args.invflags, cs.invert); - flags |= IPT_F_FRAG; + args.flags |= IPT_F_FRAG; break; case 'v': if (!verbose) set_option(&cs.options, OPT_VERBOSE, - &invflags, cs.invert); + &args.invflags, cs.invert); verbose++; break; @@ -1057,7 +1026,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) break; case 'n': - set_option(&cs.options, OPT_NUMERIC, &invflags, + set_option(&cs.options, OPT_NUMERIC, &args.invflags, cs.invert); break; @@ -1069,7 +1038,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) break; case 'x': - set_option(&cs.options, OPT_EXPANDED, &invflags, + set_option(&cs.options, OPT_EXPANDED, &args.invflags, cs.invert); break; @@ -1082,8 +1051,8 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) exit(0); case '0': - set_option(&cs.options, OPT_LINENUMBERS, &invflags, - cs.invert); + set_option(&cs.options, OPT_LINENUMBERS, + &args.invflags, cs.invert); break; case 'M': @@ -1092,38 +1061,39 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) case 'c': - set_option(&cs.options, OPT_COUNTERS, &invflags, + set_option(&cs.options, OPT_COUNTERS, &args.invflags, cs.invert); - pcnt = optarg; - bcnt = strchr(pcnt + 1, ','); - if (bcnt) - bcnt++; - if (!bcnt && optind < argc && argv[optind][0] != '-' - && argv[optind][0] != '!') - bcnt = argv[optind++]; - if (!bcnt) + args.pcnt = optarg; + args.bcnt = strchr(args.pcnt + 1, ','); + if (args.bcnt) + args.bcnt++; + if (!args.bcnt && optind < argc && + argv[optind][0] != '-' && + argv[optind][0] != '!') + args.bcnt = argv[optind++]; + if (!args.bcnt) xtables_error(PARAMETER_PROBLEM, "-%c requires packet and byte counter", opt2char(OPT_COUNTERS)); - if (sscanf(pcnt, "%llu", &pcnt_cnt) != 1) + if (sscanf(args.pcnt, "%llu", &args.pcnt_cnt) != 1) xtables_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); - if (sscanf(bcnt, "%llu", &bcnt_cnt) != 1) + if (sscanf(args.bcnt, "%llu", &args.bcnt_cnt) != 1) xtables_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(OPT_COUNTERS)); break; case '4': - if (family != AF_INET) + if (args.family != AF_INET) exit_tryhelp(2); break; case '6': - family = AF_INET6; + args.family = AF_INET6; break; case 1: /* non option */ @@ -1171,54 +1141,58 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "nothing appropriate following !"); - switch (family) { + switch (args.family) { case AF_INET: - cs.fw.ip.proto = proto; - cs.fw.ip.invflags = invflags; - cs.fw.ip.flags = flags; + cs.fw.ip.proto = args.proto; + cs.fw.ip.invflags = args.invflags; + cs.fw.ip.flags = args.flags; - strncpy(cs.fw.ip.iniface, iniface, IFNAMSIZ); + strncpy(cs.fw.ip.iniface, args.iniface, IFNAMSIZ); memcpy(cs.fw.ip.iniface_mask, - iniface_mask, IFNAMSIZ*sizeof(unsigned char)); + args.iniface_mask, IFNAMSIZ*sizeof(unsigned char)); - strncpy(cs.fw.ip.outiface, outiface, IFNAMSIZ); + strncpy(cs.fw.ip.outiface, args.outiface, IFNAMSIZ); memcpy(cs.fw.ip.outiface_mask, - outiface_mask, IFNAMSIZ*sizeof(unsigned char)); + args.outiface_mask, IFNAMSIZ*sizeof(unsigned char)); - if (goto_set) + if (args.goto_set) cs.fw.ip.flags |= IPT_F_GOTO; - cs.counters.pcnt = pcnt_cnt; - cs.counters.bcnt = bcnt_cnt; + cs.counters.pcnt = args.pcnt_cnt; + cs.counters.bcnt = args.bcnt_cnt; if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND | CMD_CHECK)) { if (!(cs.options & OPT_DESTINATION)) - dhostnetworkmask = "0.0.0.0/0"; + args.dhostnetworkmask = "0.0.0.0/0"; if (!(cs.options & OPT_SOURCE)) - shostnetworkmask = "0.0.0.0/0"; + args.shostnetworkmask = "0.0.0.0/0"; } - if (shostnetworkmask) - xtables_ipparse_multiple(shostnetworkmask, &s.addr.v4, - &s.mask.v4, &s.naddrs); - if (dhostnetworkmask) - xtables_ipparse_multiple(dhostnetworkmask, &d.addr.v4, - &d.mask.v4, &d.naddrs); - - if ((s.naddrs > 1 || d.naddrs > 1) && + if (args.shostnetworkmask) + xtables_ipparse_multiple(args.shostnetworkmask, + &args.s.addr.v4, + &args.s.mask.v4, + &args.s.naddrs); + if (args.dhostnetworkmask) + xtables_ipparse_multiple(args.dhostnetworkmask, + &args.d.addr.v4, + &args.d.mask.v4, + &args.d.naddrs); + + if ((args.s.naddrs > 1 || args.d.naddrs > 1) && (cs.fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple" " source or destination IP addresses"); break; case AF_INET6: - if (proto != 0) - flags |= IP6T_F_PROTO; + if (args.proto != 0) + args.flags |= IP6T_F_PROTO; - cs.fw6.ipv6.proto = proto; - cs.fw6.ipv6.invflags = invflags; - cs.fw6.ipv6.flags = flags; + cs.fw6.ipv6.proto = args.proto; + cs.fw6.ipv6.invflags = args.invflags; + cs.fw6.ipv6.flags = args.flags; if (is_exthdr(cs.fw6.ipv6.proto) && (cs.fw6.ipv6.invflags & XT_INV_PROTO) == 0) @@ -1227,36 +1201,40 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) "use extension match instead.\n", cs.protocol); - strncpy(cs.fw6.ipv6.iniface, iniface, IFNAMSIZ); + strncpy(cs.fw6.ipv6.iniface, args.iniface, IFNAMSIZ); memcpy(cs.fw6.ipv6.iniface_mask, - iniface_mask, IFNAMSIZ*sizeof(unsigned char)); + args.iniface_mask, IFNAMSIZ*sizeof(unsigned char)); - strncpy(cs.fw6.ipv6.outiface, outiface, IFNAMSIZ); + strncpy(cs.fw6.ipv6.outiface, args.outiface, IFNAMSIZ); memcpy(cs.fw6.ipv6.outiface_mask, - outiface_mask, IFNAMSIZ*sizeof(unsigned char)); + args.outiface_mask, IFNAMSIZ*sizeof(unsigned char)); - if (goto_set) + if (args.goto_set) cs.fw6.ipv6.flags |= IP6T_F_GOTO; - cs.fw6.counters.pcnt = pcnt_cnt; - cs.fw6.counters.bcnt = bcnt_cnt; + cs.fw6.counters.pcnt = args.pcnt_cnt; + cs.fw6.counters.bcnt = args.bcnt_cnt; if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND | CMD_CHECK)) { if (!(cs.options & OPT_DESTINATION)) - dhostnetworkmask = "::0/0"; + args.dhostnetworkmask = "::0/0"; if (!(cs.options & OPT_SOURCE)) - shostnetworkmask = "::0/0"; + args.shostnetworkmask = "::0/0"; } - if (shostnetworkmask) - xtables_ip6parse_multiple(shostnetworkmask, &s.addr.v6, - &s.mask.v6, &s.naddrs); - if (dhostnetworkmask) - xtables_ip6parse_multiple(dhostnetworkmask, &d.addr.v6, - &d.mask.v6, &d.naddrs); - - if ((s.naddrs > 1 || d.naddrs > 1) && + if (args.shostnetworkmask) + xtables_ip6parse_multiple(args.shostnetworkmask, + &args.s.addr.v6, + &args.s.mask.v6, + &args.s.naddrs); + if (args.dhostnetworkmask) + xtables_ip6parse_multiple(args.dhostnetworkmask, + &args.d.addr.v6, + &args.d.mask.v6, + &args.d.naddrs); + + if ((args.s.naddrs > 1 || args.d.naddrs > 1) && (cs.fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP))) xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple" @@ -1269,13 +1247,14 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) /* Set only if required, needed by xtables-restore */ if (h->family == AF_UNSPEC) - h->family = family; + h->family = args.family; - h->ops = nft_family_ops_lookup(family); + h->ops = nft_family_ops_lookup(args.family); if (h->ops == NULL) xtables_error(PARAMETER_PROBLEM, "Unknown family"); - if (command == CMD_REPLACE && (s.naddrs != 1 || d.naddrs != 1)) + if (command == CMD_REPLACE && + (args.s.naddrs != 1 || args.d.naddrs != 1)) xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " "specify a unique address"); @@ -1320,30 +1299,32 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) switch (command) { case CMD_APPEND: - ret = add_entry(chain, *table, &cs, family, - s, d, cs.options&OPT_VERBOSE, + ret = add_entry(chain, *table, &cs, h->family, + args.s, args.d, cs.options&OPT_VERBOSE, h, true); break; case CMD_DELETE: - ret = delete_entry(chain, *table, &cs, family, - s, d, cs.options&OPT_VERBOSE, h); + ret = delete_entry(chain, *table, &cs, h->family, + args.s, args.d, cs.options&OPT_VERBOSE, h); break; case CMD_DELETE_NUM: ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose); break; case CMD_CHECK: - ret = check_entry(chain, *table, &cs, family, - s, d, cs.options&OPT_VERBOSE, h); + ret = check_entry(chain, *table, &cs, h->family, + args.s, args.d, cs.options&OPT_VERBOSE, h); break; case CMD_REPLACE: /* FIXME replace at rulenum */ ret = replace_entry(chain, *table, &cs, rulenum - 1, - family, s, d, cs.options&OPT_VERBOSE, h); + h->family, args.s, args.d, + cs.options&OPT_VERBOSE, h); break; case CMD_INSERT: /* FIXME insert at rulenum */ - ret = add_entry(chain, *table, &cs, family, - s, d, cs.options&OPT_VERBOSE, h, false); + ret = add_entry(chain, *table, &cs, h->family, + args.s, args.d, cs.options&OPT_VERBOSE, h, + false); break; case CMD_FLUSH: ret = nft_rule_flush(h, chain, *table); @@ -1409,16 +1390,16 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) clear_rule_matches(&cs.matches); - if (family == AF_INET) { - free(s.addr.v4); - free(s.mask.v4); - free(d.addr.v4); - free(d.mask.v4); - } else if (family == AF_INET6) { - free(s.addr.v6); - free(s.mask.v6); - free(d.addr.v6); - free(d.mask.v6); + if (h->family == AF_INET) { + free(args.s.addr.v4); + free(args.s.mask.v4); + free(args.d.addr.v4); + free(args.d.mask.v4); + } else if (h->family == AF_INET6) { + free(args.s.addr.v6); + free(args.s.mask.v6); + free(args.d.addr.v6); + free(args.d.mask.v6); } xtables_free_opts(1); -- cgit v1.2.3 From 6838a7f51e6d95f904093e05e8bdc75ada70b93f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 12 May 2013 16:47:11 +0200 Subject: xtables: add new nft_ops->post_parse hook Move specific layer 3 protocol post argument parsing code to the respective nft-ipv[4|6].c files. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 51 +++++++++++++++++++- iptables/nft-ipv6.c | 67 ++++++++++++++++++++++++++ iptables/nft-shared.h | 21 ++++++++ iptables/xtables.c | 131 +------------------------------------------------- 4 files changed, 139 insertions(+), 131 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 0a601243..51ee422c 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -298,12 +298,59 @@ static uint8_t nft_ipv4_print_firewall(const struct iptables_command_state *cs, return cs->fw.ip.flags; } +static void nft_ipv4_post_parse(int command, + struct iptables_command_state *cs, + struct xtables_args *args) +{ + cs->fw.ip.proto = args->proto; + cs->fw.ip.invflags = args->invflags; + cs->fw.ip.flags = args->flags; + + strncpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ); + memcpy(cs->fw.ip.iniface_mask, + args->iniface_mask, IFNAMSIZ*sizeof(unsigned char)); + + strncpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ); + memcpy(cs->fw.ip.outiface_mask, + args->outiface_mask, IFNAMSIZ*sizeof(unsigned char)); + + if (args->goto_set) + cs->fw.ip.flags |= IPT_F_GOTO; + + cs->counters.pcnt = args->pcnt_cnt; + cs->counters.bcnt = args->bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs->options & OPT_DESTINATION)) + args->dhostnetworkmask = "0.0.0.0/0"; + if (!(cs->options & OPT_SOURCE)) + args->shostnetworkmask = "0.0.0.0/0"; + } + + if (args->shostnetworkmask) + xtables_ipparse_multiple(args->shostnetworkmask, + &args->s.addr.v4, &args->s.mask.v4, + &args->s.naddrs); + if (args->dhostnetworkmask) + xtables_ipparse_multiple(args->dhostnetworkmask, + &args->d.addr.v4, &args->d.mask.v4, + &args->d.naddrs); + + if ((args->s.naddrs > 1 || args->d.naddrs > 1) && + (cs->fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, + "! not allowed with multiple" + " source or destination IP addresses"); +} + struct nft_family_ops nft_family_ops_ipv4 = { .add = nft_ipv4_add, .is_same = nft_ipv4_is_same, .print_payload = nft_ipv4_print_payload, .parse_meta = nft_ipv4_parse_meta, .parse_payload = nft_ipv4_parse_payload, - .parse_immediate = nft_ipv4_parse_immediate, - .print_firewall = nft_ipv4_print_firewall, + .parse_immediate = nft_ipv4_parse_immediate, + .print_firewall = nft_ipv4_print_firewall, + .post_parse = nft_ipv4_post_parse, }; diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 65a89490..61c660a5 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -208,6 +208,72 @@ static uint8_t nft_ipv6_print_firewall(const struct iptables_command_state *cs, return cs->fw6.ipv6.flags; } +/* These are invalid numbers as upper layer protocol */ +static int is_exthdr(uint16_t proto) +{ + return (proto == IPPROTO_ROUTING || + proto == IPPROTO_FRAGMENT || + proto == IPPROTO_AH || + proto == IPPROTO_DSTOPTS); +} + +static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs, + struct xtables_args *args) +{ + if (args->proto != 0) + args->flags |= IP6T_F_PROTO; + + cs->fw6.ipv6.proto = args->proto; + cs->fw6.ipv6.invflags = args->invflags; + cs->fw6.ipv6.flags = args->flags; + + if (is_exthdr(cs->fw6.ipv6.proto) + && (cs->fw6.ipv6.invflags & XT_INV_PROTO) == 0) + fprintf(stderr, + "Warning: never matched protocol: %s. " + "use extension match instead.\n", + cs->protocol); + + strncpy(cs->fw6.ipv6.iniface, args->iniface, IFNAMSIZ); + memcpy(cs->fw6.ipv6.iniface_mask, + args->iniface_mask, IFNAMSIZ*sizeof(unsigned char)); + + strncpy(cs->fw6.ipv6.outiface, args->outiface, IFNAMSIZ); + memcpy(cs->fw6.ipv6.outiface_mask, + args->outiface_mask, IFNAMSIZ*sizeof(unsigned char)); + + if (args->goto_set) + cs->fw6.ipv6.flags |= IP6T_F_GOTO; + + cs->fw6.counters.pcnt = args->pcnt_cnt; + cs->fw6.counters.bcnt = args->bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs->options & OPT_DESTINATION)) + args->dhostnetworkmask = "::0/0"; + if (!(cs->options & OPT_SOURCE)) + args->shostnetworkmask = "::0/0"; + } + + if (args->shostnetworkmask) + xtables_ip6parse_multiple(args->shostnetworkmask, + &args->s.addr.v6, + &args->s.mask.v6, + &args->s.naddrs); + if (args->dhostnetworkmask) + xtables_ip6parse_multiple(args->dhostnetworkmask, + &args->d.addr.v6, + &args->d.mask.v6, + &args->d.naddrs); + + if ((args->s.naddrs > 1 || args->d.naddrs > 1) && + (cs->fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, + "! not allowed with multiple" + " source or destination IP addresses"); +} + struct nft_family_ops nft_family_ops_ipv6 = { .add = nft_ipv6_add, .is_same = nft_ipv6_is_same, @@ -216,4 +282,5 @@ struct nft_family_ops nft_family_ops_ipv6 = { .parse_payload = nft_ipv6_parse_payload, .parse_immediate = nft_ipv6_parse_immediate, .print_firewall = nft_ipv6_print_firewall, + .post_parse = nft_ipv6_post_parse, }; diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 30e87bb3..59734d9d 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -34,6 +34,8 @@ | FMT_NUMERIC | FMT_NOTABLE) #define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) +struct xtables_args; + struct nft_family_ops { int (*add)(struct nft_rule *r, struct iptables_command_state *cs); bool (*is_same)(const struct iptables_command_state *a, @@ -49,6 +51,8 @@ struct nft_family_ops { uint8_t (*print_firewall)(const struct iptables_command_state *cs, const char *targname, unsigned int num, unsigned int format); + void (*post_parse)(int command, struct iptables_command_state *cs, + struct xtables_args *args); }; void add_meta(struct nft_rule *r, uint32_t key); @@ -116,4 +120,21 @@ struct xtables_args { unsigned long long pcnt_cnt, bcnt_cnt; }; +#define CMD_NONE 0x0000U +#define CMD_INSERT 0x0001U +#define CMD_DELETE 0x0002U +#define CMD_DELETE_NUM 0x0004U +#define CMD_REPLACE 0x0008U +#define CMD_APPEND 0x0010U +#define CMD_LIST 0x0020U +#define CMD_FLUSH 0x0040U +#define CMD_ZERO 0x0080U +#define CMD_NEW_CHAIN 0x0100U +#define CMD_DELETE_CHAIN 0x0200U +#define CMD_SET_POLICY 0x0400U +#define CMD_RENAME_CHAIN 0x0800U +#define CMD_LIST_RULES 0x1000U +#define CMD_ZERO_NUM 0x2000U +#define CMD_CHECK 0x4000U + #endif diff --git a/iptables/xtables.c b/iptables/xtables.c index 6a1a1646..e3a6c802 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -50,22 +50,6 @@ #define FALSE 0 #endif -#define CMD_NONE 0x0000U -#define CMD_INSERT 0x0001U -#define CMD_DELETE 0x0002U -#define CMD_DELETE_NUM 0x0004U -#define CMD_REPLACE 0x0008U -#define CMD_APPEND 0x0010U -#define CMD_LIST 0x0020U -#define CMD_FLUSH 0x0040U -#define CMD_ZERO 0x0080U -#define CMD_NEW_CHAIN 0x0100U -#define CMD_DELETE_CHAIN 0x0200U -#define CMD_SET_POLICY 0x0400U -#define CMD_RENAME_CHAIN 0x0800U -#define CMD_LIST_RULES 0x1000U -#define CMD_ZERO_NUM 0x2000U -#define CMD_CHECK 0x4000U #define NUMBER_OF_CMD 16 static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', 'N', 'X', 'P', 'E', 'S', 'Z', 'C' }; @@ -376,15 +360,6 @@ add_command(unsigned int *cmd, const int newcmd, const int othercmds, * return global static data. */ -/* These are invalid numbers as upper layer protocol */ -static int is_exthdr(uint16_t proto) -{ - return (proto == IPPROTO_ROUTING || - proto == IPPROTO_FRAGMENT || - proto == IPPROTO_AH || - proto == IPPROTO_DSTOPTS); -} - /* Christophe Burki wants `-p 6' to imply `-m tcp'. */ /* Can't be zero. */ static int @@ -1141,110 +1116,6 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "nothing appropriate following !"); - switch (args.family) { - case AF_INET: - cs.fw.ip.proto = args.proto; - cs.fw.ip.invflags = args.invflags; - cs.fw.ip.flags = args.flags; - - strncpy(cs.fw.ip.iniface, args.iniface, IFNAMSIZ); - memcpy(cs.fw.ip.iniface_mask, - args.iniface_mask, IFNAMSIZ*sizeof(unsigned char)); - - strncpy(cs.fw.ip.outiface, args.outiface, IFNAMSIZ); - memcpy(cs.fw.ip.outiface_mask, - args.outiface_mask, IFNAMSIZ*sizeof(unsigned char)); - - if (args.goto_set) - cs.fw.ip.flags |= IPT_F_GOTO; - - cs.counters.pcnt = args.pcnt_cnt; - cs.counters.bcnt = args.bcnt_cnt; - - if (command & (CMD_REPLACE | CMD_INSERT | - CMD_DELETE | CMD_APPEND | CMD_CHECK)) { - if (!(cs.options & OPT_DESTINATION)) - args.dhostnetworkmask = "0.0.0.0/0"; - if (!(cs.options & OPT_SOURCE)) - args.shostnetworkmask = "0.0.0.0/0"; - } - - if (args.shostnetworkmask) - xtables_ipparse_multiple(args.shostnetworkmask, - &args.s.addr.v4, - &args.s.mask.v4, - &args.s.naddrs); - if (args.dhostnetworkmask) - xtables_ipparse_multiple(args.dhostnetworkmask, - &args.d.addr.v4, - &args.d.mask.v4, - &args.d.naddrs); - - if ((args.s.naddrs > 1 || args.d.naddrs > 1) && - (cs.fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) - xtables_error(PARAMETER_PROBLEM, - "! not allowed with multiple" - " source or destination IP addresses"); - break; - case AF_INET6: - if (args.proto != 0) - args.flags |= IP6T_F_PROTO; - - cs.fw6.ipv6.proto = args.proto; - cs.fw6.ipv6.invflags = args.invflags; - cs.fw6.ipv6.flags = args.flags; - - if (is_exthdr(cs.fw6.ipv6.proto) - && (cs.fw6.ipv6.invflags & XT_INV_PROTO) == 0) - fprintf(stderr, - "Warning: never matched protocol: %s. " - "use extension match instead.\n", - cs.protocol); - - strncpy(cs.fw6.ipv6.iniface, args.iniface, IFNAMSIZ); - memcpy(cs.fw6.ipv6.iniface_mask, - args.iniface_mask, IFNAMSIZ*sizeof(unsigned char)); - - strncpy(cs.fw6.ipv6.outiface, args.outiface, IFNAMSIZ); - memcpy(cs.fw6.ipv6.outiface_mask, - args.outiface_mask, IFNAMSIZ*sizeof(unsigned char)); - - if (args.goto_set) - cs.fw6.ipv6.flags |= IP6T_F_GOTO; - - cs.fw6.counters.pcnt = args.pcnt_cnt; - cs.fw6.counters.bcnt = args.bcnt_cnt; - - if (command & (CMD_REPLACE | CMD_INSERT | - CMD_DELETE | CMD_APPEND | CMD_CHECK)) { - if (!(cs.options & OPT_DESTINATION)) - args.dhostnetworkmask = "::0/0"; - if (!(cs.options & OPT_SOURCE)) - args.shostnetworkmask = "::0/0"; - } - - if (args.shostnetworkmask) - xtables_ip6parse_multiple(args.shostnetworkmask, - &args.s.addr.v6, - &args.s.mask.v6, - &args.s.naddrs); - if (args.dhostnetworkmask) - xtables_ip6parse_multiple(args.dhostnetworkmask, - &args.d.addr.v6, - &args.d.mask.v6, - &args.d.naddrs); - - if ((args.s.naddrs > 1 || args.d.naddrs > 1) && - (cs.fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP))) - xtables_error(PARAMETER_PROBLEM, - "! not allowed with multiple" - " source or destination IP addresses"); - break; - default: - exit_tryhelp(2); - break; - } - /* Set only if required, needed by xtables-restore */ if (h->family == AF_UNSPEC) h->family = args.family; @@ -1253,6 +1124,8 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) if (h->ops == NULL) xtables_error(PARAMETER_PROBLEM, "Unknown family"); + h->ops->post_parse(command, &cs, &args); + if (command == CMD_REPLACE && (args.s.naddrs != 1 || args.d.naddrs != 1)) xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " -- cgit v1.2.3 From 93373d52cb2d2963a2f3cbcec2092dcf6bddd0cf Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 12 May 2013 16:50:31 +0200 Subject: xtables: remove unused leftover definitions Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index e3a6c802..688844ec 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -158,22 +158,6 @@ static const int inverse_for_options[NUMBER_OF_OPT] = #define prog_name xtables_globals.program_name #define prog_vers xtables_globals.program_version -/* Primitive headers... */ -/* defined in netinet/in.h */ -#if 0 -#ifndef IPPROTO_ESP -#define IPPROTO_ESP 50 -#endif -#ifndef IPPROTO_AH -#define IPPROTO_AH 51 -#endif -#endif - -enum { - IPT_DOTTED_ADDR = 0, - IPT_DOTTED_MASK -}; - static void __attribute__((noreturn)) exit_tryhelp(int status) { -- cgit v1.2.3 From aa1601423175c90c37c3e6a3d6975d3e2eb74d1e Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 14 May 2013 00:52:02 +0000 Subject: xtables: initialize xtables defaults even on listing rules Output of the tool should be the same as for iptables. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index e55c18c1..7e1b47bc 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2483,6 +2483,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, struct nft_chain_list_iter *iter; struct nft_chain *c; + /* If built-in chains don't exist for this table, create them */ + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) + nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + list = nft_chain_dump(h); iter = nft_chain_list_iter_create(list); -- cgit v1.2.3 From 20c156f9f4c43857a622f015a3022517601c3600 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 14 May 2013 00:52:04 +0000 Subject: xtables: policy can be changed only on builtin chain Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 7e1b47bc..54951154 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -503,16 +503,9 @@ __nft_chain_set(struct nft_handle *h, const char *table, c = nft_chain_builtin_alloc(_t, _c, policy); if (c == NULL) return -1; - } else { - /* This is a custom chain */ - c = nft_chain_alloc(); - if (c == NULL) - return -1; - - nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); - nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); - nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy); + errno = ENOENT; + return -1; } if (counters) { -- cgit v1.2.3 From 035cf69ebfb74647fc6110e7c8cb2860e4a37162 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 22 May 2013 00:39:36 +0200 Subject: xtables: fix compilation due to missing autogenerated header Fix compilation: nft.c:51:35: fatal error: xtables-config-parser.h: File or directory doesn't exist xtables-config-parser.h was generated after compiling nft.c. Reported-by: Giuseppe Longo Tested-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables/Makefile.am b/iptables/Makefile.am index 5d653545..5cbd8ff9 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -28,10 +28,10 @@ xtables_multi_LDADD += ../libxtables/libxtables.la -lm if ENABLE_NFTABLES if HAVE_LIBMNL if HAVE_LIBNFTABLES +xtables_multi_SOURCES += xtables-config-parser.y xtables-config-syntax.l xtables_multi_SOURCES += xtables-save.c xtables-restore.c \ xtables-standalone.c xtables.c nft.c \ nft-shared.c nft-ipv4.c nft-ipv6.c \ - xtables-config-parser.y xtables-config-syntax.l \ xtables-config.c xtables-events.c xtables_multi_LDADD += -lmnl -lnftables ${libmnl_LIBS} ${libnftables_LIBS} xtables_multi_CFLAGS += -DENABLE_NFTABLES -- cgit v1.2.3 From 55bca4aa60c0027a39e635b2f05274058a2dcf2f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 31 May 2013 14:54:33 +0200 Subject: nft: don't call nft_init in nft_xtables_config_load Otherwise we keep initializing the handle over and over again. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 2 -- iptables/xtables-config.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 54951154..ca56d0ed 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2798,8 +2798,6 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, return -1; } - nft_init(h); - /* Stage 1) create tables */ titer = nft_table_list_iter_create(table_list); while ((table = nft_table_list_iter_next(titer)) != NULL) { diff --git a/iptables/xtables-config.c b/iptables/xtables-config.c index 3ad46e85..12165627 100644 --- a/iptables/xtables-config.c +++ b/iptables/xtables-config.c @@ -35,6 +35,8 @@ int xtables_config_main(int argc, char *argv[]) else filename = argv[1]; + nft_init(&h); + return nft_xtables_config_load(&h, filename, NFT_LOAD_VERBOSE) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- cgit v1.2.3 From f041efe3c26e3059df1ac8f1775f77423d4be5f6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 31 May 2013 16:21:04 +0200 Subject: xtables-restore: output the same error message that iptables-restore uses Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 2 +- iptables/xtables-restore.c | 15 ++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index ca56d0ed..7dad2464 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2641,7 +2641,7 @@ static int nft_action(struct nft_handle *h, int type) if (errno != EEXIST) perror("mnl-talk:nft_commit"); } - return ret; + return ret == 0 ? 1 : 0; } int nft_commit(struct nft_handle *h) diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 3b14a9f7..4f196fc6 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -266,22 +266,11 @@ xtables_restore_main(int argc, char *argv[]) * global commit at once, stick by now to * the existing behaviour. */ - if (nft_commit(&h)) { - fprintf(stderr, "Failed to commit " - "table %s\n", - curtable); - } DEBUGP("Calling commit\n"); - ret = 1; + ret = nft_commit(&h); } else { - if (nft_abort(&h)) { - xtables_error(OTHER_PROBLEM, - "Failed to abort " - "commit in table %s\n", - curtable); - } DEBUGP("Not calling commit, testing\n"); - ret = 1; + ret = nft_abort(&h); } in_table = 0; -- cgit v1.2.3 From d801b9f3b8161752ea2358a0bfb614603d28a8e5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 1 Jun 2013 21:14:47 +0200 Subject: xtables: fix -p protocol The protocol field in both IPv4 and IPv6 headers are 8 bits long, so we have to compare 8 bits. Reported-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 9 +++++++-- iptables/nft-shared.h | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index c368f40b..c0ee4c8a 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -91,6 +91,11 @@ void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len) nft_rule_add_expr(r, expr); } +void add_cmp_u8(struct nft_rule *r, uint8_t val, uint32_t op) +{ + add_cmp_ptr(r, op, &val, sizeof(val)); +} + void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op) { add_cmp_ptr(r, op, &val, sizeof(val)); @@ -159,7 +164,7 @@ void add_addr(struct nft_rule *r, int offset, } void add_proto(struct nft_rule *r, int offset, size_t len, - uint32_t proto, int invflags) + uint8_t proto, int invflags) { uint32_t op; @@ -170,7 +175,7 @@ void add_proto(struct nft_rule *r, int offset, size_t len, else op = NFT_CMP_EQ; - add_cmp_u32(r, proto, op); + add_cmp_u8(r, proto, op); } bool is_same_interfaces(const char *a_iniface, const char *a_outiface, diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 59734d9d..c59ab21a 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -59,6 +59,7 @@ void add_meta(struct nft_rule *r, uint32_t key); void add_payload(struct nft_rule *r, int offset, int len); void add_bitwise_u16(struct nft_rule *r, int mask, int xor); void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len); +void add_cmp_u8(struct nft_rule *r, uint8_t val, uint32_t op); void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op); void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op); void add_iniface(struct nft_rule *r, char *iface, int invflags); @@ -66,7 +67,7 @@ void add_outiface(struct nft_rule *r, char *iface, int invflags); void add_addr(struct nft_rule *r, int offset, void *data, size_t len, int invflags); void add_proto(struct nft_rule *r, int offset, size_t len, - uint32_t proto, int invflags); + uint8_t proto, int invflags); void add_compat(struct nft_rule *r, uint32_t proto, bool inv); bool is_same_interfaces(const char *a_iniface, const char *a_outiface, -- cgit v1.2.3 From d01b2c28c8101f0d24e1db3f146fd845c2a634e8 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Sat, 8 Jun 2013 02:24:35 +0000 Subject: nft: fix leak of rule and chain iterators This patch fixes the leak of chain and rule iterators. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index 7dad2464..6a4898dd 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1118,6 +1118,7 @@ next: c = nft_chain_list_iter_next(iter); } + nft_chain_list_iter_destroy(iter); nft_chain_list_free(list); return 1; @@ -1206,6 +1207,7 @@ next: r = nft_rule_list_iter_next(iter); } + nft_rule_list_iter_destroy(iter); nft_rule_list_free(list); /* the core expects 1 for success and 0 for error */ @@ -1283,6 +1285,7 @@ next: c = nft_chain_list_iter_next(iter); } + nft_chain_list_iter_destroy(iter); err: nft_chain_list_free(list); @@ -1396,6 +1399,7 @@ next: c = nft_chain_list_iter_next(iter); } + nft_chain_list_iter_destroy(iter); err: nft_chain_list_free(list); -- cgit v1.2.3 From 34c59adfae98515468ec50c644c30115fee0b97e Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Sat, 8 Jun 2013 03:40:04 +0000 Subject: nft: fix leak of chain iterator in nft_rule_list Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iptables/nft.c b/iptables/nft.c index 6a4898dd..1c7c75eb 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2524,6 +2524,7 @@ next: c = nft_chain_list_iter_next(iter); } + nft_chain_list_iter_destroy(iter); nft_chain_list_free(list); return 1; -- cgit v1.2.3 From 2c5850147937cd3da104adf654cc7b2d1f0c0a0b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 12 Jun 2013 11:49:52 +0200 Subject: nft: fix leaks in nft_xtables_config_load Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index 1c7c75eb..a67f8754 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2816,6 +2816,8 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, "table `%s' cannot be create, reason `%s'. Exitting\n", (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME), strerror(errno)); + nft_table_list_iter_destroy(titer); + nft_table_list_free(table_list); return -1; } continue; @@ -2823,6 +2825,8 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, xtables_config_perror(flags, "table `%s' has been created\n", (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); } + nft_table_list_iter_destroy(titer); + nft_table_list_free(table_list); /* Stage 2) create chains */ citer = nft_chain_list_iter_create(chain_list); @@ -2838,6 +2842,8 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, "chain `%s' cannot be create, reason `%s'. Exitting\n", (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), strerror(errno)); + nft_chain_list_iter_destroy(citer); + nft_chain_list_free(chain_list); return -1; } continue; @@ -2848,5 +2854,8 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE)); } + nft_chain_list_iter_destroy(citer); + nft_chain_list_free(chain_list); + return 0; } -- cgit v1.2.3 From b48126ca92cc44e88aa024e6da7ff245914d6a53 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 18 Jun 2013 02:29:11 +0200 Subject: xtables: allow to zero chains via -Z Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ iptables/nft.h | 1 + iptables/xtables.c | 29 ++++++++------------------ 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index a67f8754..365086bf 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2859,3 +2859,64 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, return 0; } + +int nft_chain_zero_counters(struct nft_handle *h, const char *chain, + const char *table) +{ + struct nft_chain_list *list; + struct nft_chain_list_iter *iter; + struct nft_chain *c; + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret = 0; + + list = nft_chain_list_get(h); + if (list == NULL) + goto err; + + iter = nft_chain_list_iter_create(list); + if (iter == NULL) { + DEBUGP("cannot allocate rule list iterator\n"); + return 0; + } + + c = nft_chain_list_iter_next(iter); + while (c != NULL) { + const char *chain_name = + nft_chain_attr_get(c, NFT_CHAIN_ATTR_NAME); + const char *chain_table = + nft_chain_attr_get(c, NFT_CHAIN_ATTR_TABLE); + + if (strcmp(table, chain_table) != 0) + goto next; + + if (chain != NULL && strcmp(chain, chain_name) != 0) + goto next; + + nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_PACKETS, 0); + nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_BYTES, 0); + + nft_chain_attr_unset(c, NFT_CHAIN_ATTR_HANDLE); + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, + h->family, NLM_F_ACK, h->seq); + + nft_chain_nlmsg_build_payload(nlh, c); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) + perror("mnl_talk:nft_chain_zero_counters"); + +next: + c = nft_chain_list_iter_next(iter); + } + + nft_chain_list_iter_destroy(iter); + +err: + nft_chain_list_free(list); + + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + diff --git a/iptables/nft.h b/iptables/nft.h index 8d5881d6..082260e5 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -42,6 +42,7 @@ int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, const char int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table); int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table); int nft_chain_user_rename(struct nft_handle *h, const char *chain, const char *table, const char *newname); +int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char *table); /* * Operations with rule-set. diff --git a/iptables/xtables.c b/iptables/xtables.c index 688844ec..ca057d39 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -548,14 +548,6 @@ check_entry(const char *chain, const char *table, return ret; } -static int -zero_entries(const xt_chainlabel chain, int verbose, - struct xtc_handle *handle) -{ - /* XXX iterate over chains and reset counters */ - return 1; -} - static int list_entries(struct nft_handle *h, const char *chain, const char *table, int rulenum, int verbose, int numeric, int expanded, @@ -1187,8 +1179,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) ret = nft_rule_flush(h, chain, *table); break; case CMD_ZERO: - /* FIXME */ -// ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle); + ret = nft_chain_zero_counters(h, chain, *table); break; case CMD_ZERO_NUM: /* FIXME */ @@ -1197,28 +1188,26 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) case CMD_LIST: case CMD_LIST|CMD_ZERO: case CMD_LIST|CMD_ZERO_NUM: - /* FIXME */ ret = list_entries(h, chain, *table, rulenum, cs.options&OPT_VERBOSE, cs.options&OPT_NUMERIC, cs.options&OPT_EXPANDED, cs.options&OPT_LINENUMBERS); -/* if (ret && (command & CMD_ZERO)) - ret = zero_entries(chain, - cs.options&OPT_VERBOSE, *handle); - if (ret && (command & CMD_ZERO_NUM)) + if (ret && (command & CMD_ZERO)) + ret = nft_chain_zero_counters(h, chain, *table); + /* FIXME */ +/* if (ret && (command & CMD_ZERO_NUM)) ret = iptc_zero_counter(chain, rulenum, *handle); */ break; case CMD_LIST_RULES: case CMD_LIST_RULES|CMD_ZERO: case CMD_LIST_RULES|CMD_ZERO_NUM: - /* FIXME */ ret = list_rules(h, chain, *table, rulenum, cs.options&OPT_VERBOSE); -/* if (ret && (command & CMD_ZERO)) - ret = zero_entries(chain, - cs.options&OPT_VERBOSE, *handle); - if (ret && (command & CMD_ZERO_NUM)) + if (ret && (command & CMD_ZERO)) + ret = nft_chain_zero_counters(h, chain, *table); + /* FIXME */ +/* if (ret && (command & CMD_ZERO_NUM)) ret = iptc_zero_counter(chain, rulenum, *handle); */ break; case CMD_NEW_CHAIN: -- cgit v1.2.3 From 31c46a3b9dcd1adcdd8caa6a5fe607c098e27e16 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Jun 2013 02:41:34 +0200 Subject: xtables: remove bogus comment on chain rename No longer true since Patrick added the chain rename approach back in September 2012. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 1 - 1 file changed, 1 deletion(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index ca057d39..c0585673 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -1217,7 +1217,6 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) ret = nft_chain_user_del(h, chain, *table); break; case CMD_RENAME_CHAIN: - /* XXX iptables allows renaming an used chain, we don't */ ret = nft_chain_user_rename(h, chain, *table, newname); break; case CMD_SET_POLICY: -- cgit v1.2.3 From b6d90619891e9a2f804af6af9860da8f95878abf Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Jun 2013 02:50:48 +0200 Subject: xtables: nft: remove lots of useless debugging messages While at it, fix several memleaks of list objects. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 152 ++++++++++++++++----------------------------------------- 1 file changed, 43 insertions(+), 109 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 365086bf..3aac420c 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1045,8 +1045,8 @@ static struct nft_chain_list *nft_chain_list_get(struct nft_handle *h) list = nft_chain_list_alloc(); if (list == NULL) { - DEBUGP("cannot allocate rule list\n"); - return 0; + errno = ENOMEM; + return NULL; } nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family, @@ -1096,10 +1096,8 @@ int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, struct nft_chain *c; iter = nft_chain_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return 0; - } c = nft_chain_list_iter_next(iter); while (c != NULL) { @@ -1157,10 +1155,8 @@ static struct nft_rule_list *nft_rule_list_get(struct nft_handle *h) int ret; list = nft_rule_list_alloc(); - if (list == NULL) { - DEBUGP("cannot allocate rule list\n"); + if (list == NULL) return 0; - } nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family, NLM_F_DUMP, h->seq); @@ -1182,16 +1178,12 @@ int nft_rule_save(struct nft_handle *h, const char *table, bool counters) struct nft_rule *r; list = nft_rule_list_get(h); - if (list == NULL) { - DEBUGP("cannot retrieve rule list from kernel\n"); + if (list == NULL) return 0; - } iter = nft_rule_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return 0; - } r = nft_rule_list_iter_next(iter); while (r != NULL) { @@ -1261,10 +1253,8 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table) } iter = nft_chain_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); - return 0; - } + if (iter == NULL) + goto err; c = nft_chain_list_iter_next(iter); while (c != NULL) { @@ -1305,10 +1295,8 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); c = nft_chain_alloc(); - if (c == NULL) { - DEBUGP("cannot allocate chain\n"); - return -1; - } + if (c == NULL) + return 0; nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); @@ -1368,10 +1356,8 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *tabl goto err; iter = nft_chain_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); - return 0; - } + if (iter == NULL) + goto err; c = nft_chain_list_iter_next(iter); while (c != NULL) { @@ -1419,10 +1405,8 @@ nft_chain_list_find(struct nft_handle *h, struct nft_chain_list *list, struct nft_chain *c; iter = nft_chain_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return NULL; - } c = nft_chain_list_iter_next(iter); while (c != NULL) { @@ -1452,10 +1436,8 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain) struct nft_chain_list *list; list = nft_chain_list_get(h); - if (list == NULL) { - DEBUGP("cannot allocate chain list\n"); + if (list == NULL) return NULL; - } return nft_chain_list_find(h, list, table, chain); } @@ -1483,10 +1465,8 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, /* Now prepare the new name for the chain */ c = nft_chain_alloc(); - if (c == NULL) { - DEBUGP("cannot allocate chain\n"); + if (c == NULL) return -1; - } nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)newname); @@ -1540,10 +1520,8 @@ static struct nft_table_list *nft_table_list_get(struct nft_handle *h) struct nft_table_list *list; list = nft_table_list_alloc(); - if (list == NULL) { - DEBUGP("cannot allocate table list\n"); + if (list == NULL) return 0; - } nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family, NLM_F_DUMP, h->seq); @@ -1567,10 +1545,8 @@ bool nft_table_find(struct nft_handle *h, const char *tablename) goto err; iter = nft_table_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) goto err; - } t = nft_table_list_iter_next(iter); while (t != NULL) { @@ -1605,10 +1581,8 @@ int nft_for_each_table(struct nft_handle *h, } iter = nft_table_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return 0; - } t = nft_table_list_iter_next(iter); while (t != NULL) { @@ -1634,10 +1608,8 @@ int nft_table_purge_chains(struct nft_handle *h, const char *this_table, struct nft_chain *chain_obj; iter = nft_chain_list_iter_create(chain_list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return 0; - } chain_obj = nft_chain_list_iter_next(iter); while (chain_obj != NULL) { @@ -1837,10 +1809,8 @@ static bool find_matches(struct xtables_rule_match *matches, struct nft_rule *r) int kernel_matches = 0; iter = nft_rule_expr_iter_create(r); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return false; - } expr = nft_rule_expr_iter_next(iter); while (expr != NULL) { @@ -1901,10 +1871,8 @@ find_target(struct xtables_target *target, struct nft_rule *r) return true; iter = nft_rule_expr_iter_create(r); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return false; - } expr = nft_rule_expr_iter_next(iter); while (expr != NULL) { @@ -1939,10 +1907,8 @@ find_immediate(struct nft_rule *r, const char *jumpto) struct nft_rule_expr *expr; iter = nft_rule_expr_iter_create(r); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return false; - } expr = nft_rule_expr_iter_next(iter); while (expr != NULL) { @@ -2002,15 +1968,7 @@ __nft_rule_del(struct nft_handle *h, struct nft_rule *r) static struct nft_rule_list *nft_rule_list_create(struct nft_handle *h) { - struct nft_rule_list *list; - - list = nft_rule_list_get(h); - if (list == NULL) { - DEBUGP("cannot retrieve rule list from kernel\n"); - return NULL; - } - - return list; + return nft_rule_list_get(h); } static void nft_rule_list_destroy(struct nft_rule_list *list) @@ -2028,10 +1986,8 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, bool found = false; iter = nft_rule_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return 0; - } r = nft_rule_list_iter_next(iter); while (r != NULL) { @@ -2105,10 +2061,8 @@ int nft_rule_check(struct nft_handle *h, const char *chain, nft_fn = nft_rule_check; list = nft_rule_list_create(h); - if (list == NULL) { - DEBUGP("cannot allocate rule list\n"); + if (list == NULL) return 0; - } ret = nft_rule_find(list, chain, table, e, -1) ? 1 : 0; if (ret == 0) @@ -2130,10 +2084,8 @@ int nft_rule_delete(struct nft_handle *h, const char *chain, nft_fn = nft_rule_delete; list = nft_rule_list_create(h); - if (list == NULL) { - DEBUGP("cannot allocate rule list\n"); + if (list == NULL) return 0; - } r = nft_rule_find(list, chain, table, cs, -1); if (r != NULL) { @@ -2163,10 +2115,8 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain, nft_fn = nft_rule_delete_num; list = nft_rule_list_create(h); - if (list == NULL) { - DEBUGP("cannot allocate rule list\n"); + if (list == NULL) return 0; - } r = nft_rule_find(list, chain, table, NULL, rulenum); if (r != NULL) { @@ -2197,10 +2147,8 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, nft_fn = nft_rule_replace; list = nft_rule_list_create(h); - if (list == NULL) { - DEBUGP("cannot allocate rule list\n"); + if (list == NULL) return 0; - } r = nft_rule_find(list, chain, table, cs, rulenum); if (r != NULL) { @@ -2311,10 +2259,8 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, size_t target_len = 0; iter = nft_rule_expr_iter_create(r); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return; - } expr = nft_rule_expr_iter_next(iter); while (expr != NULL) { @@ -2369,10 +2315,8 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, #endif iter = nft_rule_expr_iter_create(r); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return; - } expr = nft_rule_expr_iter_next(iter); while (expr != NULL) { @@ -2423,16 +2367,12 @@ __nft_rule_list(struct nft_handle *h, struct nft_chain *c, const char *table, const char *chain = nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); list = nft_rule_list_get(h); - if (list == NULL) { - DEBUGP("cannot retrieve rule list from kernel\n"); + if (list == NULL) return 0; - } iter = nft_rule_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); - return 0; - } + if (iter == NULL) + goto err; r = nft_rule_list_iter_next(iter); while (r != NULL) { @@ -2465,6 +2405,7 @@ next: } nft_rule_list_iter_destroy(iter); +err: nft_rule_list_free(list); if (ret == 0) @@ -2487,10 +2428,8 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, list = nft_chain_dump(h); iter = nft_chain_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); - return 0; - } + if (iter == NULL) + goto err; c = nft_chain_list_iter_next(iter); while (c != NULL) { @@ -2525,6 +2464,7 @@ next: } nft_chain_list_iter_destroy(iter); +err: nft_chain_list_free(list); return 1; @@ -2545,10 +2485,8 @@ nft_rule_list_chain_save(struct nft_handle *h, const char *table, struct nft_chain *c; iter = nft_chain_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); + if (iter == NULL) return 0; - } c = nft_chain_list_iter_next(iter); while (c != NULL) { @@ -2596,10 +2534,8 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, /* Now dump out rules in this table */ iter = nft_chain_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); - return 0; - } + if (iter == NULL) + goto err; c = nft_chain_list_iter_next(iter); while (c != NULL) { @@ -2618,7 +2554,7 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, next: c = nft_chain_list_iter_next(iter); } - +err: nft_chain_list_free(list); return 1; @@ -2875,10 +2811,8 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain, goto err; iter = nft_chain_list_iter_create(list); - if (iter == NULL) { - DEBUGP("cannot allocate rule list iterator\n"); - return 0; - } + if (iter == NULL) + goto err; c = nft_chain_list_iter_next(iter); while (c != NULL) { -- cgit v1.2.3 From 36ca9bdb288f7ba528307b7695ab94f7fa8e9a2d Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Wed, 19 Jun 2013 13:14:23 +0200 Subject: nft: break loop after found matching chain This patch breaks looping in nft_chain_user_del, nft_chain_zero_counters and nft_rule_flush after the chain is found. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index 3aac420c..8be960fb 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1271,6 +1271,8 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table) __nft_rule_flush(h, table_name, chain_name); + if (chain != NULL) + break; next: c = nft_chain_list_iter_next(iter); } @@ -1381,6 +1383,9 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *tabl break; deleted_ctr++; + + if (chain != NULL) + break; next: c = nft_chain_list_iter_next(iter); } @@ -2841,6 +2846,8 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain, if (ret < 0) perror("mnl_talk:nft_chain_zero_counters"); + if (chain != NULL) + break; next: c = nft_chain_list_iter_next(iter); } -- cgit v1.2.3 From 0a5f6c302f2f596f6e6aa0241a7772cf441b026f Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 25 Jun 2013 09:46:06 +0200 Subject: nft: print counter issues The patch fixes the counter print, missing line, and delete warnings. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 8be960fb..7fa00741 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -18,6 +18,7 @@ #include /* getprotobynumber */ #include #include +#include #include #include @@ -957,7 +958,7 @@ nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, bool counters) { if (counters) { - printf("-c %lu %lu ", + printf("-c %"PRIu64" %"PRIu64" ", nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS), nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES)); } @@ -1083,10 +1084,10 @@ static void nft_chain_print_save(struct nft_chain *c, bool basechain) if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_POLICY)) pol = nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY); - printf(":%s %s [%lu:%lu]\n", chain, policy_name[pol], + printf(":%s %s [%"PRIu64":%"PRIu64"]\n", chain, policy_name[pol], pkts, bytes); } else - printf(":%s - [%lu:%lu]\n", chain, pkts, bytes); + printf(":%s - [%"PRIu64":%"PRIu64"]\n", chain, pkts, bytes); } int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, @@ -2425,6 +2426,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, struct nft_chain_list *list; struct nft_chain_list_iter *iter; struct nft_chain *c; + bool found = false; /* If built-in chains don't exist for this table, create them */ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) @@ -2460,10 +2462,16 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, if (chain && strcmp(chain, chain_name) != 0) goto next; + if (found) + printf("\n"); + print_header(format, chain_name, policy_name[policy], &ctrs, basechain, refs); __nft_rule_list(h, c, table, rulenum, format, print_firewall); + + found = true; + next: c = nft_chain_list_iter_next(iter); } @@ -2510,7 +2518,7 @@ nft_rule_list_chain_save(struct nft_handle *h, const char *table, printf("-P %s %s", chain_name, policy_name[policy]); if (counters) { - printf(" -c %lu %lu\n", + printf(" -c %"PRIu64" %"PRIu64"\n", nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS), nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES)); } else -- cgit v1.2.3 From 137cc981906f356c971da6de13e777a419382ff4 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 25 Jun 2013 09:46:17 +0200 Subject: nft: fix another memleak in nft_rule_list_cb Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iptables/nft.c b/iptables/nft.c index 7fa00741..e660878c 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1144,6 +1144,7 @@ static int nft_rule_list_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; out: nft_rule_free(r); + nft_rule_list_free(list); err: return MNL_CB_OK; } -- cgit v1.2.3 From 9283066f1216276116b3f4f85abf18bd673a7b11 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 25 Jun 2013 11:56:55 +0200 Subject: xtables: do not proceed if nft_init fails Fix a crash if nft_init fails, it happens if nfnetlink support is not available in your Linux kernel. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-config.c | 6 +++++- iptables/xtables-restore.c | 8 +++++++- iptables/xtables-save.c | 8 +++++++- iptables/xtables-standalone.c | 8 +++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/iptables/xtables-config.c b/iptables/xtables-config.c index 12165627..515b18b1 100644 --- a/iptables/xtables-config.c +++ b/iptables/xtables-config.c @@ -35,7 +35,11 @@ int xtables_config_main(int argc, char *argv[]) else filename = argv[1]; - nft_init(&h); + if (nft_init(&h) < 0) { + fprintf(stderr, "Failed to initialize nft: %s\n", + strerror(errno)); + return EXIT_FAILURE; + } return nft_xtables_config_load(&h, filename, NFT_LOAD_VERBOSE) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 4f196fc6..a5d2a65d 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -193,7 +193,13 @@ xtables_restore_main(int argc, char *argv[]) init_extensions4(); #endif - nft_init(&h); + if (nft_init(&h) < 0) { + fprintf(stderr, "%s/%s Failed to initialize nft: %s\n", + xtables_globals.program_name, + xtables_globals.program_version, + strerror(errno)); + exit(EXIT_FAILURE); + } while ((c = getopt_long(argc, argv, "bcvthnM:T:46", options, NULL)) != -1) { switch (c) { diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 111ad4be..49b859dd 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -96,7 +96,13 @@ xtables_save_main(int argc, char *argv[]) init_extensions(); init_extensions4(); #endif - nft_init(&h); + if (nft_init(&h) < 0) { + fprintf(stderr, "%s/%s Failed to initialize nft: %s\n", + xtables_globals.program_name, + xtables_globals.program_version, + strerror(errno)); + exit(EXIT_FAILURE); + } while ((c = getopt_long(argc, argv, "bcdt:46", options, NULL)) != -1) { switch (c) { diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index 4299506a..3f8b981f 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -61,7 +61,13 @@ xtables_main(int argc, char *argv[]) init_extensions4(); #endif - nft_init(&h); + if (nft_init(&h) < 0) { + fprintf(stderr, "%s/%s Failed to initialize nft: %s\n", + xtables_globals.program_name, + xtables_globals.program_version, + strerror(errno)); + exit(EXIT_FAILURE); + } ret = do_commandx(&h, argc, argv, &table); if (!ret) { -- cgit v1.2.3 From 457819b952418501918b6e906bf5e21e3b4f9af8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 30 Jun 2013 12:34:36 +0200 Subject: xtables: fix missing afinfo configuration I noticed that the iprange match in IPv6 was broken, fix it by overriding the default family (IPv4) if -6 is passed. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-restore.c | 1 + iptables/xtables-save.c | 1 + iptables/xtables.c | 1 + 3 files changed, 3 insertions(+) diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index a5d2a65d..e66f10cd 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -233,6 +233,7 @@ xtables_restore_main(int argc, char *argv[]) break; case '6': h.family = AF_INET6; + xtables_set_nfproto(AF_INET6); break; } } diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 49b859dd..41ceaf51 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -125,6 +125,7 @@ xtables_save_main(int argc, char *argv[]) break; case '6': h.family = AF_INET6; + xtables_set_nfproto(AF_INET6); break; } } diff --git a/iptables/xtables.c b/iptables/xtables.c index c0585673..4e8d3621 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -1045,6 +1045,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) case '6': args.family = AF_INET6; + xtables_set_nfproto(AF_INET6); break; case 1: /* non option */ -- cgit v1.2.3 From 0b3bafcedff19b69ff5a51855da28e8e83c05b71 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Fri, 12 Jul 2013 13:27:33 +0200 Subject: xtables: nft: display rule by number via -L This patch fixes the display of rule by number. [ Mangled this patch not to display the header, to mimic iptables --pablo ] Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 29 ++++++++++++++++------------- iptables/xtables.c | 1 - 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index e660878c..a9a391df 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2394,19 +2394,21 @@ __nft_rule_list(struct nft_handle *h, struct nft_chain *c, const char *table, strcmp(chain, rule_chain) != 0) goto next; - if (rulenum > 0) { + if (rulenum > 0 && rule_ctr != rulenum) { /* List by rule number case */ - if (rule_ctr != rulenum) { - rule_ctr++; - goto next; - } - } else { - struct iptables_command_state cs = {}; - /* Show all rules case */ - nft_rule_to_iptables_command_state(r, &cs); + goto next; + } - cb(&cs, r, rule_ctr, format); + struct iptables_command_state cs = {}; + /* Show all rules case */ + nft_rule_to_iptables_command_state(r, &cs); + + cb(&cs, r, rule_ctr, format); + if (rulenum > 0 && rule_ctr == rulenum) { + ret = 1; + break; } + next: r = nft_rule_list_iter_next(iter); } @@ -2466,9 +2468,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, if (found) printf("\n"); - print_header(format, chain_name, policy_name[policy], &ctrs, - basechain, refs); - + if (!rulenum) { + print_header(format, chain_name, policy_name[policy], + &ctrs, basechain, refs); + } __nft_rule_list(h, c, table, rulenum, format, print_firewall); found = true; diff --git a/iptables/xtables.c b/iptables/xtables.c index 4e8d3621..c712aaf4 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -570,7 +570,6 @@ list_entries(struct nft_handle *h, const char *chain, const char *table, if (linenumbers) format |= FMT_LINENUMBERS; - /* FIXME should return found or not, and errno = ENOENT in such case */ return nft_rule_list(h, chain, table, rulenum, format); } -- cgit v1.2.3 From 10f92fce0a2ea1805c8b269543b8f1738d22bf3d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 15 Jul 2013 11:48:48 +0200 Subject: xtables: nft: display rule number via -S Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 10 ++++++---- iptables/xtables.c | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index a9a391df..3d5a1532 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2543,11 +2543,13 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, struct nft_chain_list *list; struct nft_chain_list_iter *iter; struct nft_chain *c; + int ret = 1; list = nft_chain_dump(h); /* Dump policies and custom chains first */ - nft_rule_list_chain_save(h, table, list, counters); + if (!rulenum) + nft_rule_list_chain_save(h, table, list, counters); /* Now dump out rules in this table */ iter = nft_chain_list_iter_create(list); @@ -2566,15 +2568,15 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, if (chain && strcmp(chain, chain_name) != 0) goto next; - __nft_rule_list(h, c, table, rulenum, - counters ? 0 : FMT_NOCOUNTS, list_save); + ret = __nft_rule_list(h, c, table, rulenum, + counters ? 0 : FMT_NOCOUNTS, list_save); next: c = nft_chain_list_iter_next(iter); } err: nft_chain_list_free(list); - return 1; + return ret; } static int nft_action(struct nft_handle *h, int type) diff --git a/iptables/xtables.c b/iptables/xtables.c index c712aaf4..a5a83c24 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -582,7 +582,7 @@ list_rules(struct nft_handle *h, const char *chain, const char *table, nft_rule_list_save(h, chain, table, rulenum, counters); - /* FIXME found */ + /* iptables does not return error if rule number not found */ return 1; } -- cgit v1.2.3 From d370c4ad803c37eedfbee5963fac6f7e9968939c Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 16 Jul 2013 15:38:45 +0300 Subject: nft: Set the rule family when creating a new one Fixes the debug output from (in case of ipv4 rule): DEBUG: rule: arp filter INPUT 0 to: DEBUG: rule: ip filter INPUT 0 Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iptables/nft.c b/iptables/nft.c index 3d5a1532..ec1007ed 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -690,6 +690,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, goto err; } + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, h->family); nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); -- cgit v1.2.3 From f6b30db46e5e3d32c76b186361853b5a5ecaf99f Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 16 Jul 2013 15:38:46 +0300 Subject: nft: Handle error on adding rule expressions If adding one of match/target/jumpto/verdit/counters fails, adding a rule will return an error. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 78 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index ec1007ed..50341148 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -548,7 +548,7 @@ int nft_chain_set(struct nft_handle *h, const char *table, return ret == 0 ? 1 : 0; } -static void __add_match(struct nft_rule_expr *e, struct xt_entry_match *m) +static int __add_match(struct nft_rule_expr *e, struct xt_entry_match *m) { void *info; @@ -557,25 +557,30 @@ static void __add_match(struct nft_rule_expr *e, struct xt_entry_match *m) info = calloc(1, m->u.match_size); if (info == NULL) - return; + return -ENOMEM; memcpy(info, m->data, m->u.match_size); nft_rule_expr_set(e, NFT_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m)); + + return 0; } -static void add_match(struct nft_rule *r, struct xt_entry_match *m) +static int add_match(struct nft_rule *r, struct xt_entry_match *m) { struct nft_rule_expr *expr; + int ret; expr = nft_rule_expr_alloc("match"); if (expr == NULL) - return; + return -ENOMEM; - __add_match(expr, m); + ret = __add_match(expr, m); nft_rule_add_expr(r, expr); + + return ret; } -static void __add_target(struct nft_rule_expr *e, struct xt_entry_target *t) +static int __add_target(struct nft_rule_expr *e, struct xt_entry_target *t) { void *info = NULL; @@ -586,51 +591,60 @@ static void __add_target(struct nft_rule_expr *e, struct xt_entry_target *t) if (info == NULL) { info = calloc(1, t->u.target_size); if (info == NULL) - return; + return -ENOMEM; memcpy(info, t->data, t->u.target_size); } nft_rule_expr_set(e, NFT_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t)); + + return 0; } -static void add_target(struct nft_rule *r, struct xt_entry_target *t) +static int add_target(struct nft_rule *r, struct xt_entry_target *t) { struct nft_rule_expr *expr; + int ret; expr = nft_rule_expr_alloc("target"); if (expr == NULL) - return; + return -ENOMEM; - __add_target(expr, t); + ret = __add_target(expr, t); nft_rule_add_expr(r, expr); + + return ret; } -static void add_jumpto(struct nft_rule *r, const char *name, int verdict) +static int add_jumpto(struct nft_rule *r, const char *name, int verdict) { struct nft_rule_expr *expr; expr = nft_rule_expr_alloc("immediate"); if (expr == NULL) - return; + return -ENOMEM; nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT); nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_VERDICT, verdict); nft_rule_expr_set_str(expr, NFT_EXPR_IMM_CHAIN, (char *)name); nft_rule_add_expr(r, expr); + + return 0; } -static void add_verdict(struct nft_rule *r, int verdict) +static int add_verdict(struct nft_rule *r, int verdict) { struct nft_rule_expr *expr; expr = nft_rule_expr_alloc("immediate"); if (expr == NULL) - return; + return -ENOMEM; nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT); nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_VERDICT, verdict); nft_rule_add_expr(r, expr); + + return 0; } static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) @@ -644,18 +658,20 @@ static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) #endif } -static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) +static int add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) { struct nft_rule_expr *expr; expr = nft_rule_expr_alloc("counter"); if (expr == NULL) - return; + return -ENOMEM; nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_BYTES, packets); nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_PACKETS, bytes); nft_rule_add_expr(r, expr); + + return 0; } void add_compat(struct nft_rule *r, uint32_t proto, bool inv) @@ -696,31 +712,43 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, ip_flags = h->ops->add(r, cs); - for (matchp = cs->matches; matchp; matchp = matchp->next) - add_match(r, matchp->match->m); + for (matchp = cs->matches; matchp; matchp = matchp->next) { + if (add_match(r, matchp->match->m) < 0) { + ret = 0; + goto err; + } + } /* Counters need to me added before the target, otherwise they are * increased for each rule because of the way nf_tables works. */ - add_counters(r, cs->counters.pcnt, cs->counters.bcnt); + if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) { + ret = 0; + goto err; + } /* If no target at all, add nothing (default to continue) */ if (cs->target != NULL) { /* Standard target? */ if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0) - add_verdict(r, NF_ACCEPT); + ret = add_verdict(r, NF_ACCEPT); else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) - add_verdict(r, NF_DROP); + ret = add_verdict(r, NF_DROP); else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0) - add_verdict(r, NFT_RETURN); + ret = add_verdict(r, NFT_RETURN); else - add_target(r, cs->target->t); + ret = add_target(r, cs->target->t); } else if (strlen(cs->jumpto) > 0) { /* Not standard, then it's a go / jump to chain */ if (ip_flags & IPT_F_GOTO) - add_jumpto(r, cs->jumpto, NFT_GOTO); + ret = add_jumpto(r, cs->jumpto, NFT_GOTO); else - add_jumpto(r, cs->jumpto, NFT_JUMP); + ret = add_jumpto(r, cs->jumpto, NFT_JUMP); + } + + if (ret < 0) { + ret = 0; + goto err; } /* NLM_F_CREATE autoloads the built-in table if it does not exists */ -- cgit v1.2.3 From e127d223d01aaa0886c7f279110ac36651b9a057 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 16 Jul 2013 22:07:22 +0200 Subject: xtables: Remove useless parameter to nft_chain_list_find Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 4 ++-- iptables/nft.h | 2 +- iptables/xtables-restore.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 50341148..87db9be5 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1434,7 +1434,7 @@ err: } struct nft_chain * -nft_chain_list_find(struct nft_handle *h, struct nft_chain_list *list, +nft_chain_list_find(struct nft_chain_list *list, const char *table, const char *chain) { struct nft_chain_list_iter *iter; @@ -1475,7 +1475,7 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain) if (list == NULL) return NULL; - return nft_chain_list_find(h, list, table, chain); + return nft_chain_list_find(list, table, chain); } int nft_chain_user_rename(struct nft_handle *h,const char *chain, diff --git a/iptables/nft.h b/iptables/nft.h index 082260e5..a6476714 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -37,7 +37,7 @@ struct nft_chain; int nft_chain_add(struct nft_handle *h, const struct nft_chain *c); int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters); struct nft_chain_list *nft_chain_dump(struct nft_handle *h); -struct nft_chain *nft_chain_list_find(struct nft_handle *h, struct nft_chain_list *list, const char *table, const char *chain); +struct nft_chain *nft_chain_list_find(struct nft_chain_list *list, const char *table, const char *chain); int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, const char *table); int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table); int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table); diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index e66f10cd..8469ba1a 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -326,7 +326,7 @@ xtables_restore_main(int argc, char *argv[]) exit(1); } - chain_obj = nft_chain_list_find(&h, chain_list, + chain_obj = nft_chain_list_find(chain_list, curtable, chain); /* This chain has been found, delete from list. Later * on, unvisited chains will be purged out. -- cgit v1.2.3 From 7244bef43f350ab31ef54db8a81905f6c68acac0 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 16 Jul 2013 22:07:58 +0200 Subject: nft: add function to test for a builtin chain Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 87db9be5..cb46b7a4 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -381,6 +381,14 @@ out: return ret; } +static bool nft_chain_builtin(struct nft_chain *c) +{ + /* Check if this chain has hook number, in that case is built-in. + * Should we better export the flags to user-space via nf_tables? + */ + return nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM) != NULL; +} + int nft_init(struct nft_handle *h) { h->nl = mnl_socket_open(NETLINK_NETFILTER); @@ -1138,9 +1146,7 @@ int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, if (strcmp(table, chain_table) != 0) goto next; - if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) - basechain = true; - + basechain = nft_chain_builtin(c); nft_chain_print_save(c, basechain); next: c = nft_chain_list_iter_next(iter); @@ -1368,14 +1374,6 @@ static int __nft_chain_del(struct nft_handle *h, struct nft_chain *c) return ret; } -static bool nft_chain_builtin(struct nft_chain *c) -{ - /* Check if this chain has hook number, in that case is built-in. - * Should we better export the flags to user-space via nf_tables? - */ - return nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM) != NULL; -} - int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table) { struct nft_chain_list *list; @@ -2547,7 +2545,7 @@ nft_rule_list_chain_save(struct nft_handle *h, const char *table, goto next; /* this is a base chain */ - if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) { + if (nft_chain_builtin(c)) { printf("-P %s %s", chain_name, policy_name[policy]); if (counters) { -- cgit v1.2.3 From e9a0ef8f1e27f5ef13a27f6cc984e8f2e05afd72 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 16 Jul 2013 15:38:53 +0300 Subject: nft: Fix small memory leaks Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index cb46b7a4..daa5478a 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2561,6 +2561,8 @@ next: c = nft_chain_list_iter_next(iter); } + nft_chain_list_iter_destroy(iter); + return 1; } @@ -2600,6 +2602,8 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, next: c = nft_chain_list_iter_next(iter); } + + nft_chain_list_iter_destroy(iter); err: nft_chain_list_free(list); -- cgit v1.2.3 From 1c73b54e826ffd28e94987b6ced51c4049661793 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 16 Jul 2013 21:55:15 +0200 Subject: xtables-events: print usage on wrong arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set opterr to zero to skip getopt_long error reporting. This also fixes the following compilation warning: xtables-events.c:148:13: warning: ‘print_usage’ defined but not used [-Wunused-function] Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-events.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iptables/xtables-events.c b/iptables/xtables-events.c index 2600a250..d4542f0b 100644 --- a/iptables/xtables-events.c +++ b/iptables/xtables-events.c @@ -172,11 +172,15 @@ int xtables_events_main(int argc, char *argv[]) init_extensions4(); #endif + opterr = 0; while ((c = getopt_long(argc, argv, "c", options, NULL)) != -1) { switch (c) { case 'c': counters = true; break; + default: + print_usage(argv[0], XTABLES_VERSION); + exit(EXIT_FAILURE); } } -- cgit v1.2.3 From 15539a1f5689beb00a84df87d698ee8f06824bb9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 16 Jul 2013 22:01:04 +0200 Subject: xtables-events: fix missing newline in table and chain events Add missing newline while printing table and chain events. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iptables/xtables-events.c b/iptables/xtables-events.c index d4542f0b..64ae9726 100644 --- a/iptables/xtables-events.c +++ b/iptables/xtables-events.c @@ -46,7 +46,7 @@ static int table_cb(const struct nlmsghdr *nlh, int type) nft_table_snprintf(buf, sizeof(buf), t, NFT_TABLE_O_DEFAULT, 0); /* FIXME: define syntax to represent table events */ - printf("# [table: %s]\t%s", type == NFT_MSG_NEWTABLE ? "NEW" : "DEL", buf); + printf("# [table: %s]\t%s\n", type == NFT_MSG_NEWTABLE ? "NEW" : "DEL", buf); err_free: nft_table_free(t); @@ -109,7 +109,7 @@ static int chain_cb(const struct nlmsghdr *nlh, int type) nft_chain_snprintf(buf, sizeof(buf), t, NFT_CHAIN_O_DEFAULT, 0); /* FIXME: define syntax to represent chain events */ - printf("# [chain: %s]\t%s", type == NFT_MSG_NEWCHAIN ? "NEW" : "DEL", buf); + printf("# [chain: %s]\t%s\n", type == NFT_MSG_NEWCHAIN ? "NEW" : "DEL", buf); err_free: nft_chain_free(t); -- cgit v1.2.3 From 86eed10c9f2c42e0f50eb4e527a48ee9e63146f4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 16 Jul 2013 22:18:47 +0200 Subject: nft: fix built-in chain ordering of the nat table Should be: % iptables -L -n -t nat Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination instead of: % xtables -L -n -t nat Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination Reported-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- etc/xtables.conf | 2 +- iptables/nft.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/etc/xtables.conf b/etc/xtables.conf index 1995b69f..6aee8aa8 100644 --- a/etc/xtables.conf +++ b/etc/xtables.conf @@ -20,9 +20,9 @@ family ipv4 { table nat { chain PREROUTING hook NF_INET_PRE_ROUTING prio -100 - chain POSTROUTING hook NF_INET_POST_ROUTING prio 100 chain INPUT hook NF_INET_LOCAL_IN prio -100 chain OUTPUT hook NF_INET_LOCAL_OUT prio 100 + chain POSTROUTING hook NF_INET_POST_ROUTING prio 100 } table security { diff --git a/iptables/nft.c b/iptables/nft.c index daa5478a..b9820f12 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -200,10 +200,10 @@ static struct builtin_table { .name = "nat", .chains = { { - .name = "OUTPUT", + .name = "PREROUTING", .type = "nat", .prio = -100, /* NF_IP_PRI_NAT_DST */ - .hook = NF_INET_LOCAL_OUT, + .hook = NF_INET_PRE_ROUTING, }, { .name = "INPUT", @@ -211,18 +211,18 @@ static struct builtin_table { .prio = 100, /* NF_IP_PRI_NAT_SRC */ .hook = NF_INET_LOCAL_IN, }, - { - .name = "PREROUTING", - .type = "nat", - .prio = -100, /* NF_IP_PRI_NAT_DST */ - .hook = NF_INET_PRE_ROUTING, - }, { .name = "POSTROUTING", .type = "nat", .prio = 100, /* NF_IP_PRI_NAT_SRC */ .hook = NF_INET_POST_ROUTING, }, + { + .name = "OUTPUT", + .type = "nat", + .prio = -100, /* NF_IP_PRI_NAT_DST */ + .hook = NF_INET_LOCAL_OUT, + }, }, }, }; -- cgit v1.2.3 From f77e1aca9aa8851b469f79c2db80ddb6f49253b2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 16 Jul 2013 22:43:06 +0200 Subject: src: use nft_*_list_add_tail Adapt it to the semantic fix that has been applied to libnftable nft_*_list_add now inserts nodes, instead of appending them. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 6 +++--- iptables/xtables-config-parser.y | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index b9820f12..e6702ff4 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1065,7 +1065,7 @@ static int nft_chain_list_cb(const struct nlmsghdr *nlh, void *data) goto out; } - nft_chain_list_add(c, list); + nft_chain_list_add_tail(c, list); return MNL_CB_OK; out: @@ -1174,7 +1174,7 @@ static int nft_rule_list_cb(const struct nlmsghdr *nlh, void *data) goto out; } - nft_rule_list_add(r, list); + nft_rule_list_add_tail(r, list); return MNL_CB_OK; out: @@ -1537,7 +1537,7 @@ static int nft_table_list_cb(const struct nlmsghdr *nlh, void *data) goto out; } - nft_table_list_add(t, list); + nft_table_list_add_tail(t, list); return MNL_CB_OK; out: diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y index 1d78c43c..e7a8a07e 100644 --- a/iptables/xtables-config-parser.y +++ b/iptables/xtables-config-parser.y @@ -210,6 +210,11 @@ int xtables_config_parse(char *filename, struct nft_table_list *table_list, } nft_table_attr_set_u32(table, NFT_TABLE_ATTR_FAMILY, family); nft_table_attr_set(table, NFT_TABLE_ATTR_NAME, e->data); + /* This is intentionally prepending, instead of + * appending, since the elements in the stack are in + * the reverse order that chains appear in the + * configuration file. + */ nft_table_list_add(table, table_list); break; case T_PRIO: @@ -226,6 +231,7 @@ int xtables_config_parse(char *filename, struct nft_table_list *table_list, nft_table_attr_set_u32(table, NFT_CHAIN_ATTR_FAMILY, family); nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio); nft_chain_attr_set(chain, NFT_CHAIN_ATTR_NAME, e->data); + /* Intentionally prepending, instead of appending */ nft_chain_list_add(chain, chain_list); break; case T_HOOK: -- cgit v1.2.3 From da07c930b9e2aaf8df24022a175b1774aa0bdd8b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 17 Jul 2013 14:55:01 +0200 Subject: nft: break chain listing if only one if looked for Break looping on the chain list if it finds the chain that the user requested. Based on patch from Tomasz Bursztyka. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index e6702ff4..2b9598b9 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2501,6 +2501,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, } __nft_rule_list(h, c, table, rulenum, format, print_firewall); + /* we printed the chain we wanted, stop processing. */ + if (chain) + break; + found = true; next: @@ -2599,6 +2603,10 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, ret = __nft_rule_list(h, c, table, rulenum, counters ? 0 : FMT_NOCOUNTS, list_save); + + /* we printed the chain we wanted, stop processing. */ + if (chain) + break; next: c = nft_chain_list_iter_next(iter); } -- cgit v1.2.3 From 8dd2627afc462a2591c2f621743cae1a6b98d771 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 17 Jul 2013 15:04:19 +0200 Subject: nft: fix selective chain display via -S Before: % xtables -S INPUT -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -A INPUT -p tcp -j ACCEPT After: $ xtables -S INPUT -P INPUT ACCEPT -A INPUT -p tcp -j ACCEPT Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 2b9598b9..d98b4538 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2526,8 +2526,9 @@ list_save(const struct iptables_command_state *cs, struct nft_rule *r, } static int -nft_rule_list_chain_save(struct nft_handle *h, const char *table, - struct nft_chain_list *list, int counters) +nft_rule_list_chain_save(struct nft_handle *h, const char *chain, + const char *table, struct nft_chain_list *list, + int counters) { struct nft_chain_list_iter *iter; struct nft_chain *c; @@ -2545,7 +2546,8 @@ nft_rule_list_chain_save(struct nft_handle *h, const char *table, uint32_t policy = nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY); - if (strcmp(table, chain_table) != 0) + if (strcmp(table, chain_table) != 0 || + (chain && strcmp(chain, chain_name) != 0)) goto next; /* this is a base chain */ @@ -2582,7 +2584,7 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, /* Dump policies and custom chains first */ if (!rulenum) - nft_rule_list_chain_save(h, table, list, counters); + nft_rule_list_chain_save(h, chain, table, list, counters); /* Now dump out rules in this table */ iter = nft_chain_list_iter_create(list); -- cgit v1.2.3 From cf95f347e52ca8badc6a7149045d9c09f4fa666d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 19 Jul 2013 18:42:30 +0200 Subject: xtables: add -I chain rulenum This patch adds the nft_rule_insert function, which allows us to insert rules at a given position. The function nft_rule_add has been renamed to nft_rule_append. This is possible thanks to Eric Leblond's (netfilter: nf_tables: add insert operation) kernel patch. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 158 ++++++++++++++++++++++++++++++++++++++++------------- iptables/nft.h | 3 +- iptables/xtables.c | 29 +++++++--- 3 files changed, 144 insertions(+), 46 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index d98b4538..c22e6c5b 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -689,30 +689,17 @@ void add_compat(struct nft_rule *r, uint32_t proto, bool inv) inv ? NFT_RULE_COMPAT_F_INV : 0); } -int -nft_rule_add(struct nft_handle *h, const char *chain, const char *table, - struct iptables_command_state *cs, - bool append, uint64_t handle, bool verbose) +static struct nft_rule * +nft_rule_new(struct nft_handle *h, const char *chain, const char *table, + struct iptables_command_state *cs) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; - struct xtables_rule_match *matchp; struct nft_rule *r; - int ret = 1; - int flags = append ? NLM_F_APPEND : 0; - int ip_flags = 0; - - /* If built-in chains don't exist for this table, create them */ - if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) - nft_chain_builtin_init(h, table, chain, NF_ACCEPT); - - nft_fn = nft_rule_add; + int ret = 0, ip_flags = 0; + struct xtables_rule_match *matchp; r = nft_rule_alloc(); - if (r == NULL) { - ret = 0; - goto err; - } + if (r == NULL) + return NULL; nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, h->family); nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); @@ -721,19 +708,15 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, ip_flags = h->ops->add(r, cs); for (matchp = cs->matches; matchp; matchp = matchp->next) { - if (add_match(r, matchp->match->m) < 0) { - ret = 0; + if (add_match(r, matchp->match->m) < 0) goto err; - } } /* Counters need to me added before the target, otherwise they are * increased for each rule because of the way nf_tables works. */ - if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) { - ret = 0; + if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) goto err; - } /* If no target at all, add nothing (default to continue) */ if (cs->target != NULL) { @@ -754,25 +737,50 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, ret = add_jumpto(r, cs->jumpto, NFT_JUMP); } - if (ret < 0) { + if (ret < 0) + goto err; + + return r; +err: + nft_rule_free(r); + return NULL; +} + +int +nft_rule_append(struct nft_handle *h, const char *chain, const char *table, + struct iptables_command_state *cs, uint64_t handle, + bool verbose) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_rule *r; + uint16_t flags = NLM_F_ACK|NLM_F_CREATE; + int ret = 1; + + /* If built-in chains don't exist for this table, create them */ + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) + nft_chain_builtin_init(h, table, chain, NF_ACCEPT); + + nft_fn = nft_rule_append; + + r = nft_rule_new(h, chain, table, cs); + if (r == NULL) { ret = 0; goto err; } - /* NLM_F_CREATE autoloads the built-in table if it does not exists */ - flags |= NLM_F_ACK|NLM_F_CREATE; - if (handle > 0) { nft_rule_attr_set(r, NFT_RULE_ATTR_HANDLE, &handle); flags |= NLM_F_REPLACE; - } + } else + flags |= NLM_F_APPEND; if (h->commit) { nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, NFT_RULE_F_COMMIT); } - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, - h->family, flags, h->seq); + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, + flags, h->seq); nft_rule_nlmsg_build_payload(nlh, r); @@ -782,7 +790,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, ret = mnl_talk(h, nlh, NULL, NULL); if (ret < 0) - perror("mnl_talk:nft_rule_add"); + perror("mnl_talk:nft_rule_append"); err: /* the core expects 1 for success and 0 for error */ @@ -2139,6 +2147,82 @@ int nft_rule_delete(struct nft_handle *h, const char *chain, return ret; } +static int +nft_rule_add(struct nft_handle *h, const char *chain, + const char *table, struct iptables_command_state *cs, + uint64_t handle, bool verbose) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_rule *r; + int ret = 1; + + r = nft_rule_new(h, chain, table, cs); + if (r == NULL) { + ret = 0; + goto err; + } + nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle); + + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, + NLM_F_ACK|NLM_F_CREATE, h->seq); + nft_rule_nlmsg_build_payload(nlh, r); + nft_rule_print_debug(r, nlh); + nft_rule_free(r); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) + perror("mnl_talk:nft_rule_add_num"); + +err: + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +int nft_rule_insert(struct nft_handle *h, const char *chain, + const char *table, struct iptables_command_state *cs, + int rulenum, bool verbose) +{ + struct nft_rule_list *list; + struct nft_rule *r; + uint64_t handle; + + /* If built-in chains don't exist for this table, create them */ + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) + nft_chain_builtin_init(h, table, chain, NF_ACCEPT); + + nft_fn = nft_rule_insert; + + list = nft_rule_list_create(h); + if (list == NULL) + goto err; + + r = nft_rule_find(list, chain, table, cs, rulenum); + if (r == NULL) { + errno = ENOENT; + goto err; + } + + handle = nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE); + DEBUGP("adding after rule handle %"PRIu64"\n", handle); + + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } + + nft_rule_list_destroy(list); + + return nft_rule_add(h, chain, table, cs, handle, verbose); +err: + nft_rule_list_destroy(list); + return 0; +} + int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose) { @@ -2194,9 +2278,9 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, NFT_RULE_F_COMMIT); } - ret = nft_rule_add(h, chain, table, cs, true, - nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE), - verbose); + ret = nft_rule_append(h, chain, table, cs, + nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE), + verbose); } else errno = ENOENT; diff --git a/iptables/nft.h b/iptables/nft.h index a6476714..7a6351b7 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -49,7 +49,8 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char */ struct nft_rule; -int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool append, uint64_t handle, bool verbose); +int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, uint64_t handle, bool verbose); +int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, int rulenum, bool verbose); int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose); diff --git a/iptables/xtables.c b/iptables/xtables.c index a5a83c24..41a7f716 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -404,7 +404,7 @@ static int add_entry(const char *chain, const char *table, struct iptables_command_state *cs, - int family, + int rulenum, int family, const struct addr_mask s, const struct addr_mask d, bool verbose, struct nft_handle *h, bool append) @@ -420,8 +420,15 @@ add_entry(const char *chain, cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr; cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr; - ret = nft_rule_add(h, chain, table, - cs, append, 0, verbose); + if (append) { + ret = nft_rule_append(h, chain, table, + cs, 0, + verbose); + } else { + ret = nft_rule_insert(h, chain, table, + cs, rulenum, + verbose); + } } } else if (family == AF_INET6) { memcpy(&cs->fw6.ipv6.src, @@ -433,8 +440,15 @@ add_entry(const char *chain, &d.addr.v6[j], sizeof(struct in6_addr)); memcpy(&cs->fw6.ipv6.dmsk, &d.mask.v6[j], sizeof(struct in6_addr)); - ret = nft_rule_add(h, chain, table, - cs, append, 0, verbose); + if (append) { + ret = nft_rule_append(h, chain, table, + cs, append, + verbose); + } else { + ret = nft_rule_insert(h, chain, table, + cs, rulenum, + verbose); + } } } } @@ -1148,7 +1162,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) switch (command) { case CMD_APPEND: - ret = add_entry(chain, *table, &cs, h->family, + ret = add_entry(chain, *table, &cs, 0, h->family, args.s, args.d, cs.options&OPT_VERBOSE, h, true); break; @@ -1170,8 +1184,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) cs.options&OPT_VERBOSE, h); break; case CMD_INSERT: - /* FIXME insert at rulenum */ - ret = add_entry(chain, *table, &cs, h->family, + ret = add_entry(chain, *table, &cs, rulenum - 1, h->family, args.s, args.d, cs.options&OPT_VERBOSE, h, false); break; -- cgit v1.2.3 From 71871d0a5981d2f8781307852d4f7ee66fe87600 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 19 Jul 2013 18:47:20 +0200 Subject: xtables: remove bogus comment regarding rule replacement We support rule replacement since quite some time, remove it. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 1 - 1 file changed, 1 deletion(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index 41a7f716..7a6509a7 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -1178,7 +1178,6 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) args.s, args.d, cs.options&OPT_VERBOSE, h); break; case CMD_REPLACE: - /* FIXME replace at rulenum */ ret = replace_entry(chain, *table, &cs, rulenum - 1, h->family, args.s, args.d, cs.options&OPT_VERBOSE, h); -- cgit v1.2.3 From 66a5399b6a4383ea4081d99ae852eebc1d65f265 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 24 Jul 2013 20:11:52 +0200 Subject: nft: no need for rule lookup if no position specified via -I Improve (dc3d8a6 xtables: add -I chain rulenum) to avoid a look up for the rule if no rule number is specified via -I. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index c22e6c5b..9a857b9e 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2162,7 +2162,8 @@ nft_rule_add(struct nft_handle *h, const char *chain, ret = 0; goto err; } - nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle); + if (handle > 0) + nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle); if (h->commit) { nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, @@ -2189,7 +2190,7 @@ int nft_rule_insert(struct nft_handle *h, const char *chain, { struct nft_rule_list *list; struct nft_rule *r; - uint64_t handle; + uint64_t handle = 0; /* If built-in chains don't exist for this table, create them */ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) @@ -2197,26 +2198,23 @@ int nft_rule_insert(struct nft_handle *h, const char *chain, nft_fn = nft_rule_insert; - list = nft_rule_list_create(h); - if (list == NULL) - goto err; + if (rulenum > 0) { + list = nft_rule_list_create(h); + if (list == NULL) + goto err; - r = nft_rule_find(list, chain, table, cs, rulenum); - if (r == NULL) { - errno = ENOENT; - goto err; - } + r = nft_rule_find(list, chain, table, cs, rulenum); + if (r == NULL) { + errno = ENOENT; + goto err; + } - handle = nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE); - DEBUGP("adding after rule handle %"PRIu64"\n", handle); + handle = nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE); + DEBUGP("adding after rule handle %"PRIu64"\n", handle); - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); + nft_rule_list_destroy(list); } - nft_rule_list_destroy(list); - return nft_rule_add(h, chain, table, cs, handle, verbose); err: nft_rule_list_destroy(list); -- cgit v1.2.3 From afae1f841bc2c4b39a38fa97d271f3877d00bf3a Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Fri, 26 Jul 2013 13:05:15 +0200 Subject: nft: associate table configuration to handle via nft_init We need family dependent built-in table/chain configuration. This patch is a step forward making nft family independent in order to support arptables and ebtables compatibility layers. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 35 ++++++++++------------------------- iptables/nft.h | 24 +++++++++++++++++++++++- iptables/xtables-config.c | 2 +- iptables/xtables-restore.c | 2 +- iptables/xtables-save.c | 2 +- iptables/xtables-standalone.c | 2 +- 6 files changed, 37 insertions(+), 30 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 9a857b9e..68fc1539 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -80,24 +80,7 @@ static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, return 0; } -#define FILTER 0 -#define MANGLE 1 -#define RAW 2 -#define SECURITY 3 -#define NAT 4 -#define TABLES_MAX 5 - -struct builtin_chain { - const char *name; - const char *type; - uint32_t prio; - uint32_t hook; -}; - -static struct builtin_table { - const char *name; - struct builtin_chain chains[NF_INET_NUMHOOKS]; -} tables[TABLES_MAX] = { +struct builtin_table xtables_ipv4[TABLES_MAX] = { [RAW] = { .name = "raw", .chains = { @@ -305,20 +288,21 @@ nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, } /* find if built-in table already exists */ -static struct builtin_table *nft_table_builtin_find(const char *table) +static struct builtin_table +*nft_table_builtin_find(struct nft_handle *h, const char *table) { int i; bool found = false; for (i=0; itables[i].name, table) != 0) continue; found = true; break; } - return found ? &tables[i] : NULL; + return found ? &h->tables[i] : NULL; } /* find if built-in chain already exists */ @@ -366,7 +350,7 @@ nft_chain_builtin_init(struct nft_handle *h, const char *table, int ret = 0; struct builtin_table *t; - t = nft_table_builtin_find(table); + t = nft_table_builtin_find(h, table); if (t == NULL) { ret = -1; goto out; @@ -389,7 +373,7 @@ static bool nft_chain_builtin(struct nft_chain *c) return nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM) != NULL; } -int nft_init(struct nft_handle *h) +int nft_init(struct nft_handle *h, struct builtin_table *t) { h->nl = mnl_socket_open(NETLINK_NETFILTER); if (h->nl == NULL) { @@ -402,6 +386,7 @@ int nft_init(struct nft_handle *h) return -1; } h->portid = mnl_socket_get_portid(h->nl); + h->tables = t; return 0; } @@ -440,7 +425,7 @@ int nft_table_set_dormant(struct nft_handle *h, const char *table) int ret = 0, i; struct builtin_table *t; - t = nft_table_builtin_find(table); + t = nft_table_builtin_find(h, table); if (t == NULL) { ret = -1; goto out; @@ -501,7 +486,7 @@ __nft_chain_set(struct nft_handle *h, const char *table, struct builtin_chain *_c; int ret; - _t = nft_table_builtin_find(table); + _t = nft_table_builtin_find(h, table); /* if this built-in table does not exists, create it */ if (_t != NULL) nft_table_builtin_add(h, _t, false); diff --git a/iptables/nft.h b/iptables/nft.h index 7a6351b7..f3317c92 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -4,6 +4,25 @@ #include "xshared.h" #include "nft-shared.h" +#define FILTER 0 +#define MANGLE 1 +#define RAW 2 +#define SECURITY 3 +#define NAT 4 +#define TABLES_MAX 5 + +struct builtin_chain { + const char *name; + const char *type; + uint32_t prio; + uint32_t hook; +}; + +struct builtin_table { + const char *name; + struct builtin_chain chains[NF_INET_NUMHOOKS]; +}; + struct nft_handle { int family; struct mnl_socket *nl; @@ -11,9 +30,12 @@ struct nft_handle { uint32_t seq; bool commit; struct nft_family_ops *ops; + struct builtin_table *tables; }; -int nft_init(struct nft_handle *h); +extern struct builtin_table xtables_ipv4[TABLES_MAX]; + +int nft_init(struct nft_handle *h, struct builtin_table *t); void nft_fini(struct nft_handle *h); /* diff --git a/iptables/xtables-config.c b/iptables/xtables-config.c index 515b18b1..b7cf6094 100644 --- a/iptables/xtables-config.c +++ b/iptables/xtables-config.c @@ -35,7 +35,7 @@ int xtables_config_main(int argc, char *argv[]) else filename = argv[1]; - if (nft_init(&h) < 0) { + if (nft_init(&h, xtables_ipv4) < 0) { fprintf(stderr, "Failed to initialize nft: %s\n", strerror(errno)); return EXIT_FAILURE; diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 8469ba1a..608e189b 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -193,7 +193,7 @@ xtables_restore_main(int argc, char *argv[]) init_extensions4(); #endif - if (nft_init(&h) < 0) { + if (nft_init(&h, xtables_ipv4) < 0) { fprintf(stderr, "%s/%s Failed to initialize nft: %s\n", xtables_globals.program_name, xtables_globals.program_version, diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 41ceaf51..db030901 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -96,7 +96,7 @@ xtables_save_main(int argc, char *argv[]) init_extensions(); init_extensions4(); #endif - if (nft_init(&h) < 0) { + if (nft_init(&h, xtables_ipv4) < 0) { fprintf(stderr, "%s/%s Failed to initialize nft: %s\n", xtables_globals.program_name, xtables_globals.program_version, diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index 3f8b981f..9d5a6675 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -61,7 +61,7 @@ xtables_main(int argc, char *argv[]) init_extensions4(); #endif - if (nft_init(&h) < 0) { + if (nft_init(&h, xtables_ipv4) < 0) { fprintf(stderr, "%s/%s Failed to initialize nft: %s\n", xtables_globals.program_name, xtables_globals.program_version, -- cgit v1.2.3 From 96180491d51853a4315ba4eeb29a53505b6515e5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jul 2013 11:28:45 +0200 Subject: xtables: fix typo in add_entry for the IPv6 case It should pass zero, instead of the 'append' boolean. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index 7a6509a7..30355466 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -442,7 +442,7 @@ add_entry(const char *chain, &d.mask.v6[j], sizeof(struct in6_addr)); if (append) { ret = nft_rule_append(h, chain, table, - cs, append, + cs, 0, verbose); } else { ret = nft_rule_insert(h, chain, table, -- cgit v1.2.3 From 85512f09680a798ebe92e96ad62eeae863fbc791 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jul 2013 11:39:59 +0200 Subject: nft: fix match revision lookup for IPv6 IPT_SO_GET_REVISION_MATCH != IP6T_SO_GET_REVISION_MATCH, thus, the revision infrastructure was looking for targets instead matches. --- iptables/nft.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iptables/nft.c b/iptables/nft.c index 68fc1539..bbe5e396 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2730,7 +2730,8 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt) uint32_t portid, seq, type; int ret = 0; - if (opt == IPT_SO_GET_REVISION_MATCH) + if (opt == IPT_SO_GET_REVISION_MATCH || + opt == IP6T_SO_GET_REVISION_MATCH) type = 0; else type = 1; -- cgit v1.2.3 From c0c3cf9cf8065ade1d525da417e08d0f8d6bc359 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Fri, 26 Jul 2013 13:05:19 +0200 Subject: nft: fix family operation lookup xtables-restore -6 was using the IPv4 family, instead of IPv6 as it should be. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index 30355466..946f04d6 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -1110,7 +1110,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) if (h->family == AF_UNSPEC) h->family = args.family; - h->ops = nft_family_ops_lookup(args.family); + h->ops = nft_family_ops_lookup(h->family); if (h->ops == NULL) xtables_error(PARAMETER_PROBLEM, "Unknown family"); -- cgit v1.2.3 From 2bf54fd4eca0ea19e8effeb487d87b2e5a2cdc10 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jul 2013 20:17:44 +0200 Subject: etc: add default IPv6 table and chain definitions Add definition of table and chains to the optional xtables.conf file. Signed-off-by: Pablo Neira Ayuso --- etc/xtables.conf | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/etc/xtables.conf b/etc/xtables.conf index 6aee8aa8..7b2b8e5f 100644 --- a/etc/xtables.conf +++ b/etc/xtables.conf @@ -26,8 +26,42 @@ family ipv4 { } table security { - chain INPUT hook NF_INET_LOCAL_IN prio 150 - chain FORWARD hook NF_INET_FORWARD prio 150 - chain OUTPUT hook NF_INET_LOCAL_OUT prio 150 + chain INPUT hook NF_INET_LOCAL_IN prio 50 + chain FORWARD hook NF_INET_FORWARD prio 50 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 50 + } +} + +family ipv6 { + table raw { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -300 + chain OUTPUT hook NF_INET_LOCAL_OUT prio -300 + } + + table mangle { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -150 + chain INPUT hook NF_INET_LOCAL_IN prio -150 + chain FORWARD hook NF_INET_FORWARD prio -150 + chain OUTPUT hook NF_INET_LOCAL_OUT prio -150 + chain POSTROUTING hook NF_INET_POST_ROUTING prio -150 + } + + table filter { + chain INPUT hook NF_INET_LOCAL_IN prio 0 + chain FORWARD hook NF_INET_FORWARD prio 0 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 0 + } + + table nat { + chain PREROUTING hook NF_INET_PRE_ROUTING prio -100 + chain INPUT hook NF_INET_LOCAL_IN prio -100 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 100 + chain POSTROUTING hook NF_INET_POST_ROUTING prio 100 + } + + table security { + chain INPUT hook NF_INET_LOCAL_IN prio 50 + chain FORWARD hook NF_INET_FORWARD prio 50 + chain OUTPUT hook NF_INET_LOCAL_OUT prio 50 } } -- cgit v1.2.3 From cea310bd1d078618e87b83410f8c6f75b34de450 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 30 Jul 2013 14:03:46 +0300 Subject: nft: load only the tables of the current family This changes nft_xtables_config_load() permit to load only the tables of the current family. [ This patch includes a fix for the configuration parser that I detected while testing this patch --pablo ] Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 17 +++++++++++++++++ iptables/xtables-config-parser.y | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/iptables/nft.c b/iptables/nft.c index bbe5e396..df4122cb 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2853,6 +2853,8 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, struct nft_chain_list_iter *citer; struct nft_table *table; struct nft_chain *chain; + uint32_t table_family, chain_family; + bool found = false; if (xtables_config_parse(filename, table_list, chain_list) < 0) { if (errno == ENOENT) { @@ -2870,6 +2872,13 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, /* Stage 1) create tables */ titer = nft_table_list_iter_create(table_list); while ((table = nft_table_list_iter_next(titer)) != NULL) { + table_family = nft_table_attr_get_u32(table, + NFT_TABLE_ATTR_FAMILY); + if (h->family != table_family) + continue; + + found = true; + if (nft_table_add(h, table) < 0) { if (errno == EEXIST) { xtables_config_perror(flags, @@ -2892,9 +2901,17 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, nft_table_list_iter_destroy(titer); nft_table_list_free(table_list); + if (!found) + return -1; + /* Stage 2) create chains */ citer = nft_chain_list_iter_create(chain_list); while ((chain = nft_chain_list_iter_next(citer)) != NULL) { + chain_family = nft_chain_attr_get_u32(chain, + NFT_CHAIN_ATTR_TABLE); + if (h->family != chain_family) + continue; + if (nft_chain_add(h, chain) < 0) { if (errno == EEXIST) { xtables_config_perror(flags, diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y index e7a8a07e..36dae38d 100644 --- a/iptables/xtables-config-parser.y +++ b/iptables/xtables-config-parser.y @@ -228,7 +228,8 @@ int xtables_config_parse(char *filename, struct nft_table_list *table_list, } nft_chain_attr_set(chain, NFT_CHAIN_ATTR_TABLE, (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); - nft_table_attr_set_u32(table, NFT_CHAIN_ATTR_FAMILY, family); + nft_chain_attr_set_u32(chain, NFT_CHAIN_ATTR_FAMILY, + nft_table_attr_get_u32(table, NFT_TABLE_ATTR_FAMILY)); nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio); nft_chain_attr_set(chain, NFT_CHAIN_ATTR_NAME, e->data); /* Intentionally prepending, instead of appending */ -- cgit v1.2.3 From f8781d3da3943a0fc10212e77cdbcd0697943596 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Wed, 31 Jul 2013 15:18:52 +0300 Subject: xtables: Do not dump before command parsing has been finished On xtables-save.c, -d will not output right away but after the full command line hase been parsed. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-save.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index db030901..990c0fe6 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -78,6 +78,7 @@ int xtables_save_main(int argc, char *argv[]) { const char *tablename = NULL; + bool dump = false; struct nft_handle h = { .family = AF_INET, /* default to AF_INET */ }; @@ -118,8 +119,8 @@ xtables_save_main(int argc, char *argv[]) xtables_modprobe_program = optarg; break; case 'd': - do_output(&h, tablename, show_counters); - exit(0); + dump = true; + break; case '4': h.family = AF_INET; break; @@ -135,5 +136,10 @@ xtables_save_main(int argc, char *argv[]) exit(1); } + if (dump) { + do_output(&h, tablename, show_counters); + exit(0); + } + return !do_output(&h, tablename, show_counters); } -- cgit v1.2.3 From c846f1ab08906c7673dfc5b79cd3d20219f0b359 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 25 Jul 2013 20:16:21 +0300 Subject: nft: Remove useless function Likely to be a leftover from the initial bootstrap. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index df4122cb..3db050e4 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1661,33 +1661,6 @@ next: return 0; } -static inline int -match_different(const struct xt_entry_match *a, - const unsigned char *a_elems, - const unsigned char *b_elems, - unsigned char **maskptr) -{ - const struct xt_entry_match *b; - unsigned int i; - - /* Offset of b is the same as a. */ - b = (void *)b_elems + ((unsigned char *)a - a_elems); - - if (a->u.match_size != b->u.match_size) - return 1; - - if (strcmp(a->u.user.name, b->u.user.name) != 0) - return 1; - - *maskptr += XT_ALIGN(sizeof(*a)); - - for (i = 0; i < a->u.match_size - XT_ALIGN(sizeof(*a)); i++) - if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0) - return 1; - *maskptr += i; - return 0; -} - static void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, struct iptables_command_state *cs) -- cgit v1.2.3 From b08836a33bac28860400e1e34c8244249f651a6f Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 1 Aug 2013 20:44:34 +0300 Subject: nft: Optimize rule listing when chain and rulenum are provided Thus, we can save some cycles by not dumping the entire chain list and looping on it. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 3db050e4..cd7f4f1f 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2428,7 +2428,7 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, } static int -__nft_rule_list(struct nft_handle *h, struct nft_chain *c, const char *table, +__nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format, void (*cb)(const struct iptables_command_state *cs, struct nft_rule *r, unsigned int num, @@ -2438,7 +2438,6 @@ __nft_rule_list(struct nft_handle *h, struct nft_chain *c, const char *table, struct nft_rule_list_iter *iter; struct nft_rule *r; int rule_ctr = 0, ret = 0; - const char *chain = nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME); list = nft_rule_list_get(h); if (list == NULL) @@ -2502,6 +2501,12 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + if (chain && rulenum) { + __nft_rule_list(h, chain, table, + rulenum, format, print_firewall); + return 1; + } + list = nft_chain_dump(h); iter = nft_chain_list_iter_create(list); @@ -2539,7 +2544,8 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, print_header(format, chain_name, policy_name[policy], &ctrs, basechain, refs); } - __nft_rule_list(h, c, table, rulenum, format, print_firewall); + __nft_rule_list(h, chain_name, table, + rulenum, format, print_firewall); /* we printed the chain we wanted, stop processing. */ if (chain) @@ -2643,7 +2649,7 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, if (chain && strcmp(chain, chain_name) != 0) goto next; - ret = __nft_rule_list(h, c, table, rulenum, + ret = __nft_rule_list(h, chain_name, table, rulenum, counters ? 0 : FMT_NOCOUNTS, list_save); /* we printed the chain we wanted, stop processing. */ -- cgit v1.2.3 From 8aa384a7d54734fd830840a2593cd4f07749976f Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 1 Aug 2013 20:44:35 +0300 Subject: nft: Make internal rule listing callback more generic This will be useful for reusing nft_rule_list in other tools such as xtables-arptables. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index cd7f4f1f..a87d3031 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2318,9 +2318,9 @@ print_match(struct nft_rule_expr *expr, int numeric) } static void -print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, - unsigned int num, unsigned int format) +print_firewall(struct nft_rule *r, unsigned int num, unsigned int format) { + struct iptables_command_state cs = {}; const struct xtables_target *target = NULL; const char *targname = NULL; const void *targinfo = NULL; @@ -2332,6 +2332,8 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, struct xt_entry_target *t; size_t target_len = 0; + nft_rule_to_iptables_command_state(r, &cs); + iter = nft_rule_expr_iter_create(r); if (iter == NULL) return; @@ -2378,7 +2380,7 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); ops = nft_family_ops_lookup(family); - flags = ops->print_firewall(cs, targname, num, format); + flags = ops->print_firewall(&cs, targname, num, format); if (format & FMT_NOTABLE) fputs(" ", stdout); @@ -2430,8 +2432,7 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, static int __nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format, - void (*cb)(const struct iptables_command_state *cs, - struct nft_rule *r, unsigned int num, + void (*cb)(struct nft_rule *r, unsigned int num, unsigned int format)) { struct nft_rule_list *list; @@ -2465,11 +2466,7 @@ __nft_rule_list(struct nft_handle *h, const char *chain, const char *table, goto next; } - struct iptables_command_state cs = {}; - /* Show all rules case */ - nft_rule_to_iptables_command_state(r, &cs); - - cb(&cs, r, rule_ctr, format); + cb(r, rule_ctr, format); if (rulenum > 0 && rule_ctr == rulenum) { ret = 1; break; @@ -2565,8 +2562,7 @@ err: } static void -list_save(const struct iptables_command_state *cs, struct nft_rule *r, - unsigned int num, unsigned int format) +list_save(struct nft_rule *r, unsigned int num, unsigned int format) { nft_rule_print_save(r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS)); } -- cgit v1.2.3 From 7a1026f59c101a67233c65dd5ef9b0ae15945ca5 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Wed, 7 Aug 2013 11:31:35 +0300 Subject: nft: Remove useless test on rulenum in nft_rule_list() Rulenum is already handled before the loop, making this test useless. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index a87d3031..15c50a75 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2537,10 +2537,9 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, if (found) printf("\n"); - if (!rulenum) { - print_header(format, chain_name, policy_name[policy], + print_header(format, chain_name, policy_name[policy], &ctrs, basechain, refs); - } + __nft_rule_list(h, chain_name, table, rulenum, format, print_firewall); -- cgit v1.2.3 From e23e66f9d1a25c75df684850b7cd99053708c4d0 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Wed, 7 Aug 2013 11:31:36 +0300 Subject: nft: Generalize nft_rule_list() against current family Now, firewall rule printing is done through nft_family_ops .print_firewall function. This moves generic part for ipv4 and ipv6 into nft-shared.c, and enables reusing nft_rule_list() for other family such as ARP which will be useful for arptables compatibility tool. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 39 ++++++-- iptables/nft-ipv6.c | 39 ++++++-- iptables/nft-shared.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++++ iptables/nft-shared.h | 13 ++- iptables/nft.c | 249 +------------------------------------------------- 5 files changed, 315 insertions(+), 264 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 51ee422c..81be9f4c 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -284,18 +284,41 @@ static void print_ipv4_addr(const struct iptables_command_state *cs, } -static uint8_t nft_ipv4_print_firewall(const struct iptables_command_state *cs, - const char *targname, unsigned int num, - unsigned int format) +static void nft_ipv4_print_firewall(struct nft_rule *r, unsigned int num, + unsigned int format) { - print_firewall_details(cs, targname, cs->fw.ip.flags, - cs->fw.ip.invflags, cs->fw.ip.proto, - cs->fw.ip.iniface, cs->fw.ip.outiface, + struct iptables_command_state cs = {}; + const char *targname = NULL; + const void *targinfo = NULL; + size_t target_len = 0; + + nft_rule_to_iptables_command_state(r, &cs); + + targname = nft_parse_target(r, &targinfo, &target_len); + + print_firewall_details(&cs, targname, cs.fw.ip.flags, + cs.fw.ip.invflags, cs.fw.ip.proto, + cs.fw.ip.iniface, cs.fw.ip.outiface, num, format); - print_ipv4_addr(cs, format); + print_ipv4_addr(&cs, format); - return cs->fw.ip.flags; + if (format & FMT_NOTABLE) + fputs(" ", stdout); + +#ifdef IPT_F_GOTO + if (cs.fw.ip.flags & IPT_F_GOTO) + printf("[goto] "); +#endif + + if (print_matches(r, format) != 0) + return; + + if (print_target(targname, targinfo, target_len, format) != 0) + return; + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); } static void nft_ipv4_post_parse(int command, diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 61c660a5..0214dcf2 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -194,18 +194,41 @@ static void print_ipv6_addr(const struct iptables_command_state *cs, } } -static uint8_t nft_ipv6_print_firewall(const struct iptables_command_state *cs, - const char *targname, unsigned int num, - unsigned int format) +static void nft_ipv6_print_firewall(struct nft_rule *r, unsigned int num, + unsigned int format) { - print_firewall_details(cs, targname, cs->fw6.ipv6.flags, - cs->fw6.ipv6.invflags, cs->fw6.ipv6.proto, - cs->fw6.ipv6.iniface, cs->fw6.ipv6.outiface, + struct iptables_command_state cs = {}; + const char *targname = NULL; + const void *targinfo = NULL; + size_t target_len = 0; + + nft_rule_to_iptables_command_state(r, &cs); + + targname = nft_parse_target(r, &targinfo, &target_len); + + print_firewall_details(&cs, targname, cs.fw6.ipv6.flags, + cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto, + cs.fw6.ipv6.iniface, cs.fw6.ipv6.outiface, num, format); - print_ipv6_addr(cs, format); + print_ipv6_addr(&cs, format); - return cs->fw6.ipv6.flags; + if (format & FMT_NOTABLE) + fputs(" ", stdout); + +#ifdef IPT_F_GOTO + if (cs.fw6.ipv6.flags & IPT_F_GOTO) + printf("[goto] "); +#endif + + if (print_matches(r, format) != 0) + return; + + if (print_target(targname, targinfo, target_len, format) != 0) + return; + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); } /* These are invalid numbers as upper layer protocol */ diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index c0ee4c8a..4a0317bc 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -12,8 +12,12 @@ #include #include +#include #include #include +#include + +#include #include @@ -277,6 +281,59 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, } } +const char *nft_parse_target(struct nft_rule *r, const void **targinfo, + size_t *target_len) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + const char *targname = NULL; + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) + return NULL; + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "target") == 0) { + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_TG_NAME); + *targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, + target_len); + break; + } else if (strcmp(name, "immediate") == 0) { + uint32_t verdict = + nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); + + switch(verdict) { + case NF_ACCEPT: + targname = "ACCEPT"; + break; + case NF_DROP: + targname = "DROP"; + break; + case NFT_RETURN: + targname = "RETURN"; + break; + case NFT_GOTO: + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_IMM_CHAIN); + break; + case NFT_JUMP: + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_IMM_CHAIN); + break; + } + } + expr = nft_rule_expr_iter_next(iter); + } + nft_rule_expr_iter_destroy(iter); + + return targname; +} + void print_proto(uint16_t proto, int invert) { const struct protoent *pent = getprotobynumber(proto); @@ -318,6 +375,188 @@ void get_cmp_data(struct nft_rule_expr_iter *iter, *inv = false; } +static void +nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + int family, struct iptables_command_state *cs) +{ + uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); + struct nft_family_ops *ops = nft_family_ops_lookup(family); + const char *name; + + e = nft_rule_expr_iter_next(iter); + if (e == NULL) + return; + + name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); + if (strcmp(name, "cmp") != 0) { + DEBUGP("skipping no cmp after meta\n"); + return; + } + + ops->parse_meta(e, key, cs); +} + +static void +nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + int family, struct iptables_command_state *cs) +{ + struct nft_family_ops *ops = nft_family_ops_lookup(family); + uint32_t offset; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + ops->parse_payload(iter, cs, offset); +} + +static void +nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + struct xt_counters *counters) +{ + counters->pcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS); + counters->bcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES); +} + +static void +nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + int family, struct iptables_command_state *cs) +{ + int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); + const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); + struct nft_family_ops *ops; + + /* Standard target? */ + switch(verdict) { + case NF_ACCEPT: + cs->jumpto = "ACCEPT"; + return; + case NF_DROP: + cs->jumpto = "DROP"; + return; + case NFT_RETURN: + cs->jumpto = "RETURN"; + return; + case NFT_GOTO: + ops = nft_family_ops_lookup(family); + ops->parse_immediate(cs); + case NFT_JUMP: + cs->jumpto = chain; + return; + } +} + +void nft_rule_to_iptables_command_state(struct nft_rule *r, + struct iptables_command_state *cs) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) + return; + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "counter") == 0) { + nft_parse_counter(expr, iter, &cs->counters); + } else if (strcmp(name, "payload") == 0) { + nft_parse_payload(expr, iter, family, cs); + } else if (strcmp(name, "meta") == 0) { + nft_parse_meta(expr, iter, family, cs); + } else if (strcmp(name, "immediate") == 0) { + nft_parse_immediate(expr, iter, family, cs); + } + + expr = nft_rule_expr_iter_next(iter); + } + + nft_rule_expr_iter_destroy(iter); +} + +static void +print_match(struct nft_rule_expr *expr, int numeric) +{ + size_t len; + const char *match_name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); + const void *match_info = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len); + const struct xtables_match *match = + xtables_find_match(match_name, XTF_TRY_LOAD, NULL); + struct xt_entry_match *m = + calloc(1, sizeof(struct xt_entry_match) + len); + + /* emulate struct xt_entry_match since ->print needs it */ + memcpy((void *)&m->data, match_info, len); + + if (match) { + if (match->print) + /* FIXME missing first parameter */ + match->print(NULL, m, numeric); + else + printf("%s ", match_name); + } else { + if (match_name[0]) + printf("UNKNOWN match `%s' ", match_name); + } + + free(m); +} + +int print_matches(struct nft_rule *r, int format) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) + return -ENOMEM; + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "match") == 0) + print_match(expr, format & FMT_NUMERIC); + + expr = nft_rule_expr_iter_next(iter); + } + nft_rule_expr_iter_destroy(iter); + + return 0; +} + +int print_target(const char *targname, const void *targinfo, + size_t target_len, int format) +{ + struct xtables_target *target; + struct xt_entry_target *t; + + if (targname == NULL) + return 0; + + t = calloc(1, sizeof(struct xt_entry_target) + target_len); + if (t == NULL) + return -ENOMEM; + + /* emulate struct xt_entry_target since ->print needs it */ + memcpy((void *)&t->data, targinfo, target_len); + + target = xtables_find_target(targname, XTF_TRY_LOAD); + if (target) { + if (target->print) + /* FIXME missing first parameter */ + target->print(NULL, t, format & FMT_NUMERIC); + } else + printf("[%ld bytes of unknown target data] ", target_len); + + free(t); + + return 0; +} + void print_num(uint64_t number, unsigned int format) { if (format & FMT_KILOMEGAGIGA) { diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index c59ab21a..488ed632 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -48,9 +48,8 @@ struct nft_family_ops { struct iptables_command_state *cs, uint32_t offset); void (*parse_immediate)(struct iptables_command_state *cs); - uint8_t (*print_firewall)(const struct iptables_command_state *cs, - const char *targname, unsigned int num, - unsigned int format); + void (*print_firewall)(struct nft_rule *r, unsigned int num, + unsigned int format); void (*post_parse)(int command, struct iptables_command_state *cs, struct xtables_args *args); }; @@ -80,10 +79,16 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface, void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, unsigned char *iniface_mask, char *outiface, unsigned char *outiface_mask, uint8_t *invflags); - +const char *nft_parse_target(struct nft_rule *r, const void **targinfo, + size_t *target_len); void print_proto(uint16_t proto, int invert); void get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv); +void nft_rule_to_iptables_command_state(struct nft_rule *r, + struct iptables_command_state *cs); +int print_matches(struct nft_rule *r, int format); +int print_target(const char *targname, const void *targinfo, + size_t target_len, int format); void print_num(uint64_t number, unsigned int format); void print_firewall_details(const struct iptables_command_state *cs, const char *targname, uint8_t flags, diff --git a/iptables/nft.c b/iptables/nft.c index 15c50a75..28e71d8f 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1661,108 +1661,6 @@ next: return 0; } -static void -nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - int family, struct iptables_command_state *cs) -{ - uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); - struct nft_family_ops *ops = nft_family_ops_lookup(family); - const char *name; - - e = nft_rule_expr_iter_next(iter); - if (e == NULL) - return; - - name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); - if (strcmp(name, "cmp") != 0) { - DEBUGP("skipping no cmp after meta\n"); - return; - } - - ops->parse_meta(e, key, cs); -} - -static void -nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - int family, struct iptables_command_state *cs) -{ - struct nft_family_ops *ops = nft_family_ops_lookup(family); - uint32_t offset; - - offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - - ops->parse_payload(iter, cs, offset); -} - -static void -nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - struct xt_counters *counters) -{ - counters->pcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS); - counters->bcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES); -} - -static void -nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - int family, struct iptables_command_state *cs) -{ - int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); - const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); - struct nft_family_ops *ops; - - /* Standard target? */ - switch(verdict) { - case NF_ACCEPT: - cs->jumpto = "ACCEPT"; - return; - case NF_DROP: - cs->jumpto = "DROP"; - return; - case NFT_RETURN: - cs->jumpto = "RETURN"; - return; - case NFT_GOTO: - ops = nft_family_ops_lookup(family); - ops->parse_immediate(cs); - case NFT_JUMP: - cs->jumpto = chain; - return; - } -} - -static void -nft_rule_to_iptables_command_state(struct nft_rule *r, - struct iptables_command_state *cs) -{ - struct nft_rule_expr_iter *iter; - struct nft_rule_expr *expr; - int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); - - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) - return; - - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); - - if (strcmp(name, "counter") == 0) { - nft_parse_counter(expr, iter, &cs->counters); - } else if (strcmp(name, "payload") == 0) { - nft_parse_payload(expr, iter, family, cs); - } else if (strcmp(name, "meta") == 0) { - nft_parse_meta(expr, iter, family, cs); - } else if (strcmp(name, "immediate") == 0) { - nft_parse_immediate(expr, iter, family, cs); - } - - expr = nft_rule_expr_iter_next(iter); - } - - nft_rule_expr_iter_destroy(iter); -} - static int matches_howmany(struct xtables_rule_match *matches) { struct xtables_rule_match *matchp; @@ -2289,146 +2187,6 @@ print_header(unsigned int format, const char *chain, const char *pol, printf("\n"); } -static void -print_match(struct nft_rule_expr *expr, int numeric) -{ - size_t len; - const char *match_name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); - const void *match_info = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len); - const struct xtables_match *match = - xtables_find_match(match_name, XTF_TRY_LOAD, NULL); - struct xt_entry_match *m = - calloc(1, sizeof(struct xt_entry_match) + len); - - /* emulate struct xt_entry_match since ->print needs it */ - memcpy((void *)&m->data, match_info, len); - - if (match) { - if (match->print) - /* FIXME missing first parameter */ - match->print(NULL, m, numeric); - else - printf("%s ", match_name); - } else { - if (match_name[0]) - printf("UNKNOWN match `%s' ", match_name); - } - - free(m); -} - -static void -print_firewall(struct nft_rule *r, unsigned int num, unsigned int format) -{ - struct iptables_command_state cs = {}; - const struct xtables_target *target = NULL; - const char *targname = NULL; - const void *targinfo = NULL; - int family; - struct nft_family_ops *ops; - uint8_t flags = 0; - struct nft_rule_expr_iter *iter; - struct nft_rule_expr *expr; - struct xt_entry_target *t; - size_t target_len = 0; - - nft_rule_to_iptables_command_state(r, &cs); - - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) - return; - - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); - - if (strcmp(name, "target") == 0) { - targname = nft_rule_expr_get_str(expr, - NFT_EXPR_TG_NAME); - targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, - &target_len); - break; - } else if (strcmp(name, "immediate") == 0) { - uint32_t verdict = - nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); - - switch(verdict) { - case NF_ACCEPT: - targname = "ACCEPT"; - break; - case NF_DROP: - targname = "DROP"; - break; - case NFT_RETURN: - targname = "RETURN"; - break; - case NFT_GOTO: - targname = nft_rule_expr_get_str(expr, - NFT_EXPR_IMM_CHAIN); - break; - case NFT_JUMP: - targname = nft_rule_expr_get_str(expr, - NFT_EXPR_IMM_CHAIN); - break; - } - } - expr = nft_rule_expr_iter_next(iter); - } - nft_rule_expr_iter_destroy(iter); - - family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); - ops = nft_family_ops_lookup(family); - - flags = ops->print_firewall(&cs, targname, num, format); - - if (format & FMT_NOTABLE) - fputs(" ", stdout); - -#ifdef IPT_F_GOTO - if(flags & IPT_F_GOTO) - printf("[goto] "); -#endif - - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) - return; - - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); - - if (strcmp(name, "match") == 0) - print_match(expr, format & FMT_NUMERIC); - - expr = nft_rule_expr_iter_next(iter); - } - nft_rule_expr_iter_destroy(iter); - - t = calloc(1, sizeof(struct xt_entry_target) + target_len); - if (t == NULL) - return; - - /* emulate struct xt_entry_match since ->print needs it */ - memcpy((void *)&t->data, targinfo, target_len); - - if (targname) { - target = xtables_find_target(targname, XTF_TRY_LOAD); - if (target) { - if (target->print) - /* FIXME missing first parameter */ - target->print(NULL, t, format & FMT_NUMERIC); - } else - printf("[%ld bytes of unknown target data] ", - target_len); - } - free(t); - - if (!(format & FMT_NONEWLINE)) - fputc('\n', stdout); -} - static int __nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format, @@ -2489,6 +2247,7 @@ err: int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format) { + const struct nft_family_ops *ops; struct nft_chain_list *list; struct nft_chain_list_iter *iter; struct nft_chain *c; @@ -2498,9 +2257,11 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + ops = nft_family_ops_lookup(h->family); + if (chain && rulenum) { __nft_rule_list(h, chain, table, - rulenum, format, print_firewall); + rulenum, format, ops->print_firewall); return 1; } @@ -2541,7 +2302,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, &ctrs, basechain, refs); __nft_rule_list(h, chain_name, table, - rulenum, format, print_firewall); + rulenum, format, ops->print_firewall); /* we printed the chain we wanted, stop processing. */ if (chain) -- cgit v1.2.3 From c167e01221fc8541240b5c0505516e35a216d0eb Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Wed, 7 Aug 2013 11:31:37 +0300 Subject: nft: Print unknown target data only when relevant Bug is: xtables -N test xtables -A FORWARD -j test xtables -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination test all -- anywhere anywhere [0 bytes of unknown target data] Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain test (1 references) target prot opt source destination "[0 bytes of unknown target data]" should not be printed in this case. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 4a0317bc..dd4766b0 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -549,7 +549,7 @@ int print_target(const char *targname, const void *targinfo, if (target->print) /* FIXME missing first parameter */ target->print(NULL, t, format & FMT_NUMERIC); - } else + } else if (target_len > 0) printf("[%ld bytes of unknown target data] ", target_len); free(t); -- cgit v1.2.3 From c11ad7cce0d7195e12347bd4a3092ac24e19f8b4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 9 Aug 2013 17:51:31 +0200 Subject: xtables: use xtables_rule_matches_free Thus, we can kill clear_rule_matches. Not required since we are based upon 1.4.19.1. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/iptables/xtables.c b/iptables/xtables.c index 946f04d6..3e6092f2 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -600,27 +600,6 @@ list_rules(struct nft_handle *h, const char *chain, const char *table, return 1; } -static void clear_rule_matches(struct xtables_rule_match **matches) -{ - struct xtables_rule_match *matchp, *tmp; - - for (matchp = *matches; matchp;) { - tmp = matchp->next; - if (matchp->match->m) { - free(matchp->match->m); - matchp->match->m = NULL; - } - if (matchp->match == matchp->match->next) { - free(matchp->match); - matchp->match = NULL; - } - free(matchp); - matchp = tmp; - } - - *matches = NULL; -} - static void command_jump(struct iptables_command_state *cs) { size_t size; @@ -1245,7 +1224,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) /* if (verbose > 1) dump_entries(*handle); */ - clear_rule_matches(&cs.matches); + xtables_rule_matches_free(&cs.matches); if (h->family == AF_INET) { free(args.s.addr.v4); -- cgit v1.2.3 From cdc78b1d6bd7b48ec05d78fc6e6cd98473f40357 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Mon, 19 Aug 2013 15:04:02 +0300 Subject: nft: convert rule into a command state structure This helps to reduce the code complexity to have one single common path for printing, saving and looking up for the rule. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 99 ++++------ iptables/nft-ipv6.c | 85 +++------ iptables/nft-shared.c | 272 +++++++++++++------------- iptables/nft-shared.h | 16 +- iptables/nft.c | 477 +++++++--------------------------------------- iptables/nft.h | 4 +- iptables/xtables-events.c | 11 +- 7 files changed, 303 insertions(+), 661 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 81be9f4c..08a8c653 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -121,14 +121,6 @@ static void get_frag(struct nft_rule_expr_iter *iter, bool *inv) *inv = false; } -static void print_frag(bool inv) -{ - if (inv) - printf("! -f "); - else - printf("-f "); -} - static const char *mask_to_str(uint32_t mask) { static char mask_str[sizeof("255.255.255.255")]; @@ -155,50 +147,6 @@ static const char *mask_to_str(uint32_t mask) return mask_str; } -static void nft_ipv4_print_payload(struct nft_rule_expr *e, - struct nft_rule_expr_iter *iter) -{ - uint32_t offset; - bool inv; - - offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - - switch(offset) { - struct in_addr addr; - uint8_t proto; - - case offsetof(struct iphdr, saddr): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - if (inv) - printf("! -s %s/%s ", inet_ntoa(addr), - mask_to_str(0xffffffff)); - else - printf("-s %s/%s ", inet_ntoa(addr), - mask_to_str(0xffffffff)); - break; - case offsetof(struct iphdr, daddr): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - if (inv) - printf("! -d %s/%s ", inet_ntoa(addr), - mask_to_str(0xffffffff)); - else - printf("-d %s/%s ", inet_ntoa(addr), - mask_to_str(0xffffffff)); - break; - case offsetof(struct iphdr, protocol): - get_cmp_data(iter, &proto, sizeof(proto), &inv); - print_proto(proto, inv); - break; - case offsetof(struct iphdr, frag_off): - get_frag(iter, &inv); - print_frag(inv); - break; - default: - DEBUGP("unknown payload offset %d\n", offset); - break; - } -} - static void nft_ipv4_parse_meta(struct nft_rule_expr *e, uint8_t key, struct iptables_command_state *cs) { @@ -288,15 +236,10 @@ static void nft_ipv4_print_firewall(struct nft_rule *r, unsigned int num, unsigned int format) { struct iptables_command_state cs = {}; - const char *targname = NULL; - const void *targinfo = NULL; - size_t target_len = 0; nft_rule_to_iptables_command_state(r, &cs); - targname = nft_parse_target(r, &targinfo, &target_len); - - print_firewall_details(&cs, targname, cs.fw.ip.flags, + print_firewall_details(&cs, cs.jumpto, cs.fw.ip.flags, cs.fw.ip.invflags, cs.fw.ip.proto, cs.fw.ip.iniface, cs.fw.ip.outiface, num, format); @@ -311,16 +254,44 @@ static void nft_ipv4_print_firewall(struct nft_rule *r, unsigned int num, printf("[goto] "); #endif - if (print_matches(r, format) != 0) - return; - - if (print_target(targname, targinfo, target_len, format) != 0) - return; + print_matches_and_target(&cs, format); if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); } +static void save_ipv4_addr(char letter, const struct in_addr *addr, + uint32_t mask, int invert) +{ + if (!mask && !invert && !addr->s_addr) + return; + + printf("%s-%c %s/%s ", invert ? "! " : "", letter, inet_ntoa(*addr), + mask_to_str(mask)); +} + +static uint8_t nft_ipv4_save_firewall(const struct iptables_command_state *cs, + unsigned int format) +{ + save_firewall_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto, + cs->fw.ip.iniface, cs->fw.ip.iniface_mask, + cs->fw.ip.outiface, cs->fw.ip.outiface_mask, + format); + + if (cs->fw.ip.flags & IPT_F_FRAG) { + if (cs->fw.ip.invflags & IPT_INV_FRAG) + printf("! "); + printf("-f "); + } + + save_ipv4_addr('s', &cs->fw.ip.src, cs->fw.ip.smsk.s_addr, + cs->fw.ip.invflags & IPT_INV_SRCIP); + save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr, + cs->fw.ip.invflags & IPT_INV_DSTIP); + + return cs->fw.ip.flags; +} + static void nft_ipv4_post_parse(int command, struct iptables_command_state *cs, struct xtables_args *args) @@ -370,10 +341,10 @@ static void nft_ipv4_post_parse(int command, struct nft_family_ops nft_family_ops_ipv4 = { .add = nft_ipv4_add, .is_same = nft_ipv4_is_same, - .print_payload = nft_ipv4_print_payload, .parse_meta = nft_ipv4_parse_meta, .parse_payload = nft_ipv4_parse_payload, .parse_immediate = nft_ipv4_parse_immediate, .print_firewall = nft_ipv4_print_firewall, + .save_firewall = nft_ipv4_save_firewall, .post_parse = nft_ipv4_post_parse, }; diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 0214dcf2..91ef255c 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -69,48 +69,6 @@ static bool nft_ipv6_is_same(const struct iptables_command_state *a, b->fw6.ipv6.outiface_mask); } -static void nft_ipv6_print_payload(struct nft_rule_expr *e, - struct nft_rule_expr_iter *iter) -{ - uint32_t offset; - bool inv; - - offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - - switch (offset) { - char addr_str[INET6_ADDRSTRLEN]; - struct in6_addr addr; - uint8_t proto; - case offsetof(struct ip6_hdr, ip6_src): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); - - if (inv) - printf("! -s %s ", addr_str); - else - printf("-s %s ", addr_str); - - break; - case offsetof(struct ip6_hdr, ip6_dst): - get_cmp_data(iter, &addr, sizeof(addr), &inv); - inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); - - if (inv) - printf("! -d %s ", addr_str); - else - printf("-d %s ", addr_str); - - break; - case offsetof(struct ip6_hdr, ip6_nxt): - get_cmp_data(iter, &proto, sizeof(proto), &inv); - print_proto(proto, inv); - break; - default: - DEBUGP("unknown payload offset %d\n", offset); - break; - } -} - static void nft_ipv6_parse_meta(struct nft_rule_expr *e, uint8_t key, struct iptables_command_state *cs) { @@ -198,15 +156,10 @@ static void nft_ipv6_print_firewall(struct nft_rule *r, unsigned int num, unsigned int format) { struct iptables_command_state cs = {}; - const char *targname = NULL; - const void *targinfo = NULL; - size_t target_len = 0; nft_rule_to_iptables_command_state(r, &cs); - targname = nft_parse_target(r, &targinfo, &target_len); - - print_firewall_details(&cs, targname, cs.fw6.ipv6.flags, + print_firewall_details(&cs, cs.jumpto, cs.fw6.ipv6.flags, cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto, cs.fw6.ipv6.iniface, cs.fw6.ipv6.outiface, num, format); @@ -221,16 +174,40 @@ static void nft_ipv6_print_firewall(struct nft_rule *r, unsigned int num, printf("[goto] "); #endif - if (print_matches(r, format) != 0) - return; - - if (print_target(targname, targinfo, target_len, format) != 0) - return; + print_matches_and_target(&cs, format); if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); } +static void save_ipv6_addr(char letter, const struct in6_addr *addr, + int invert) +{ + char addr_str[INET6_ADDRSTRLEN]; + + if (!invert && IN6_IS_ADDR_UNSPECIFIED(addr)) + return; + + inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN); + printf("%s-%c %s ", invert ? "! " : "", letter, addr_str); +} + +static uint8_t nft_ipv6_save_firewall(const struct iptables_command_state *cs, + unsigned int format) +{ + save_firewall_details(cs, cs->fw6.ipv6.invflags, cs->fw6.ipv6.proto, + cs->fw6.ipv6.iniface, cs->fw6.ipv6.iniface_mask, + cs->fw6.ipv6.outiface, cs->fw6.ipv6.outiface_mask, + format); + + save_ipv6_addr('s', &cs->fw6.ipv6.src, + cs->fw6.ipv6.invflags & IPT_INV_SRCIP); + save_ipv6_addr('d', &cs->fw6.ipv6.dst, + cs->fw6.ipv6.invflags & IPT_INV_DSTIP); + + return cs->fw6.ipv6.flags; +} + /* These are invalid numbers as upper layer protocol */ static int is_exthdr(uint16_t proto) { @@ -300,10 +277,10 @@ static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs, struct nft_family_ops nft_family_ops_ipv6 = { .add = nft_ipv6_add, .is_same = nft_ipv6_is_same, - .print_payload = nft_ipv6_print_payload, .parse_meta = nft_ipv6_parse_meta, .parse_payload = nft_ipv6_parse_payload, .parse_immediate = nft_ipv6_parse_immediate, .print_firewall = nft_ipv6_print_firewall, + .save_firewall = nft_ipv6_save_firewall, .post_parse = nft_ipv6_post_parse, }; diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index dd4766b0..ad5e80ea 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -281,57 +281,60 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, } } -const char *nft_parse_target(struct nft_rule *r, const void **targinfo, - size_t *target_len) +static void +nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) { - struct nft_rule_expr_iter *iter; - struct nft_rule_expr *expr; - const char *targname = NULL; + size_t tg_len; + const char *targname = nft_rule_expr_get_str(e, NFT_EXPR_TG_NAME); + const void *targinfo = nft_rule_expr_get(e, NFT_EXPR_TG_INFO, &tg_len); + struct xtables_target *target; + struct xt_entry_target *t; - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) - return NULL; + target = xtables_find_target(targname, XTF_TRY_LOAD); + if (target == NULL) + return; - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + t = calloc(1, sizeof(struct xt_entry_target) + tg_len); + if (t == NULL) { + fprintf(stderr, "OOM"); + exit(EXIT_FAILURE); + } + memcpy(&t->data, targinfo, tg_len); + t->u.target_size = tg_len + XT_ALIGN(sizeof(struct xt_entry_target)); + t->u.user.revision = nft_rule_expr_get_u32(e, NFT_EXPR_TG_REV); + strcpy(t->u.user.name, target->name); - if (strcmp(name, "target") == 0) { - targname = nft_rule_expr_get_str(expr, - NFT_EXPR_TG_NAME); - *targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, - target_len); - break; - } else if (strcmp(name, "immediate") == 0) { - uint32_t verdict = - nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); - - switch(verdict) { - case NF_ACCEPT: - targname = "ACCEPT"; - break; - case NF_DROP: - targname = "DROP"; - break; - case NFT_RETURN: - targname = "RETURN"; - break; - case NFT_GOTO: - targname = nft_rule_expr_get_str(expr, - NFT_EXPR_IMM_CHAIN); - break; - case NFT_JUMP: - targname = nft_rule_expr_get_str(expr, - NFT_EXPR_IMM_CHAIN); - break; - } - } - expr = nft_rule_expr_iter_next(iter); + target->t = t; + cs->target = target; +} + +static void +nft_parse_match(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) +{ + size_t mt_len; + const char *mt_name = nft_rule_expr_get_str(e, NFT_EXPR_MT_NAME); + const void *mt_info = nft_rule_expr_get(e, NFT_EXPR_MT_INFO, &mt_len); + struct xtables_match *match; + struct xt_entry_match *m; + + match = xtables_find_match(mt_name, XTF_TRY_LOAD, &cs->matches); + if (match == NULL) + return; + + m = calloc(1, sizeof(struct xt_entry_match) + mt_len); + if (m == NULL) { + fprintf(stderr, "OOM"); + exit(EXIT_FAILURE); } - nft_rule_expr_iter_destroy(iter); - return targname; + memcpy(&m->data, mt_info, mt_len); + m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match)); + m->u.user.revision = nft_rule_expr_get_u32(e, NFT_EXPR_TG_REV); + strcpy(m->u.user.name, match->name); + + match->m = m; } void print_proto(uint16_t proto, int invert) @@ -460,101 +463,30 @@ void nft_rule_to_iptables_command_state(struct nft_rule *r, const char *name = nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); - if (strcmp(name, "counter") == 0) { + if (strcmp(name, "counter") == 0) nft_parse_counter(expr, iter, &cs->counters); - } else if (strcmp(name, "payload") == 0) { + else if (strcmp(name, "payload") == 0) nft_parse_payload(expr, iter, family, cs); - } else if (strcmp(name, "meta") == 0) { + else if (strcmp(name, "meta") == 0) nft_parse_meta(expr, iter, family, cs); - } else if (strcmp(name, "immediate") == 0) { + else if (strcmp(name, "immediate") == 0) nft_parse_immediate(expr, iter, family, cs); - } + else if (strcmp(name, "match") == 0) + nft_parse_match(expr, iter, cs); + else if (strcmp(name, "target") == 0) + nft_parse_target(expr, iter, cs); expr = nft_rule_expr_iter_next(iter); } nft_rule_expr_iter_destroy(iter); -} - -static void -print_match(struct nft_rule_expr *expr, int numeric) -{ - size_t len; - const char *match_name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); - const void *match_info = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len); - const struct xtables_match *match = - xtables_find_match(match_name, XTF_TRY_LOAD, NULL); - struct xt_entry_match *m = - calloc(1, sizeof(struct xt_entry_match) + len); - - /* emulate struct xt_entry_match since ->print needs it */ - memcpy((void *)&m->data, match_info, len); - - if (match) { - if (match->print) - /* FIXME missing first parameter */ - match->print(NULL, m, numeric); - else - printf("%s ", match_name); - } else { - if (match_name[0]) - printf("UNKNOWN match `%s' ", match_name); - } - - free(m); -} - -int print_matches(struct nft_rule *r, int format) -{ - struct nft_rule_expr_iter *iter; - struct nft_rule_expr *expr; - - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) - return -ENOMEM; - - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); - - if (strcmp(name, "match") == 0) - print_match(expr, format & FMT_NUMERIC); - - expr = nft_rule_expr_iter_next(iter); - } - nft_rule_expr_iter_destroy(iter); - - return 0; -} - -int print_target(const char *targname, const void *targinfo, - size_t target_len, int format) -{ - struct xtables_target *target; - struct xt_entry_target *t; - - if (targname == NULL) - return 0; - - t = calloc(1, sizeof(struct xt_entry_target) + target_len); - if (t == NULL) - return -ENOMEM; - - /* emulate struct xt_entry_target since ->print needs it */ - memcpy((void *)&t->data, targinfo, target_len); - - target = xtables_find_target(targname, XTF_TRY_LOAD); - if (target) { - if (target->print) - /* FIXME missing first parameter */ - target->print(NULL, t, format & FMT_NUMERIC); - } else if (target_len > 0) - printf("[%ld bytes of unknown target data] ", target_len); - free(t); - - return 0; + if (cs->target != NULL) + cs->jumpto = cs->target->name; + else if (cs->jumpto != NULL) + cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD); + else + cs->jumpto = ""; } void print_num(uint64_t number, unsigned int format) @@ -646,6 +578,86 @@ void print_firewall_details(const struct iptables_command_state *cs, } } +static void +print_iface(char letter, const char *iface, const unsigned char *mask, int inv) +{ + unsigned int i; + + if (mask[0] == 0) + return; + + printf("%s-%c ", inv ? "! " : "", letter); + + for (i = 0; i < IFNAMSIZ; i++) { + if (mask[i] != 0) { + if (iface[i] != '\0') + printf("%c", iface[i]); + } else { + if (iface[i-1] != '\0') + printf("+"); + break; + } + } + + printf(" "); +} + +void save_firewall_details(const struct iptables_command_state *cs, + uint8_t invflags, uint16_t proto, + const char *iniface, + unsigned const char *iniface_mask, + const char *outiface, + unsigned const char *outiface_mask, + unsigned int format) +{ + if (!(format & FMT_NOCOUNTS)) { + printf("-c "); + print_num(cs->counters.pcnt, format); + print_num(cs->counters.bcnt, format); + } + + if (iniface != NULL) { + print_iface('i', iniface, iniface_mask, + invflags & IPT_INV_VIA_IN); + } + if (outiface != NULL) { + print_iface('o', outiface, outiface_mask, + invflags & IPT_INV_VIA_OUT); + } + + if (proto > 0) { + const struct protoent *pent = getprotobynumber(proto); + + if (invflags & XT_INV_PROTO) + printf("! "); + + if (pent) + printf("-p %s ", pent->p_name); + else + printf("-p %u ", proto); + } +} + +void print_matches_and_target(struct iptables_command_state *cs, + unsigned int format) +{ + struct xtables_rule_match *matchp; + + for (matchp = cs->matches; matchp; matchp = matchp->next) { + if (matchp->match->print != NULL) { + matchp->match->print(NULL, matchp->match->m, + format & FMT_NUMERIC); + } + } + + if (cs->target != NULL) { + if (cs->target->print != NULL) { + cs->target->print(NULL, cs->target->t, + format & FMT_NUMERIC); + } + } +} + struct nft_family_ops *nft_family_ops_lookup(int family) { switch (family) { diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 488ed632..e77b303d 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -50,6 +50,8 @@ struct nft_family_ops { void (*parse_immediate)(struct iptables_command_state *cs); void (*print_firewall)(struct nft_rule *r, unsigned int num, unsigned int format); + uint8_t (*save_firewall)(const struct iptables_command_state *cs, + unsigned int format); void (*post_parse)(int command, struct iptables_command_state *cs, struct xtables_args *args); }; @@ -79,22 +81,26 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface, void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, unsigned char *iniface_mask, char *outiface, unsigned char *outiface_mask, uint8_t *invflags); -const char *nft_parse_target(struct nft_rule *r, const void **targinfo, - size_t *target_len); void print_proto(uint16_t proto, int invert); void get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv); void nft_rule_to_iptables_command_state(struct nft_rule *r, struct iptables_command_state *cs); -int print_matches(struct nft_rule *r, int format); -int print_target(const char *targname, const void *targinfo, - size_t target_len, int format); void print_num(uint64_t number, unsigned int format); void print_firewall_details(const struct iptables_command_state *cs, const char *targname, uint8_t flags, uint8_t invflags, uint8_t proto, const char *iniface, const char *outiface, unsigned int num, unsigned int format); +void print_matches_and_target(struct iptables_command_state *cs, + unsigned int format); +void save_firewall_details(const struct iptables_command_state *cs, + uint8_t invflags, uint16_t proto, + const char *iniface, + unsigned const char *iniface_mask, + const char *outiface, + unsigned const char *outiface_mask, + unsigned int format); struct nft_family_ops *nft_family_ops_lookup(int family); diff --git a/iptables/nft.c b/iptables/nft.c index 28e71d8f..3b393cc7 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -782,224 +782,16 @@ err: return ret == 0 ? 1 : 0; } -static void nft_match_save(struct nft_rule_expr *expr) -{ - const char *name; - const struct xtables_match *match; - struct xt_entry_match *emu; - const void *mtinfo; - size_t len; - - name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); - - match = xtables_find_match(name, XTF_TRY_LOAD, NULL); - if (match == NULL) - return; - - mtinfo = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len); - if (mtinfo == NULL) - return; - - emu = calloc(1, sizeof(struct xt_entry_match) + len); - if (emu == NULL) - return; - - memcpy(&emu->data, mtinfo, len); - - if (match->alias) - printf("-m %s", match->alias(emu)); - else - printf("-m %s", match->name); - - /* FIXME missing parameter */ - if (match->save) - match->save(NULL, emu); - - printf(" "); - - free(emu); -} - -static void nft_target_save(struct nft_rule_expr *expr) -{ - const char *name; - const struct xtables_target *target; - struct xt_entry_target *emu; - const void *tginfo; - size_t len; - - name = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME); - - /* Standard target not supported, we use native immediate expression */ - if (strcmp(name, "") == 0) { - printf("ERROR: standard target seen, should not happen\n"); - return; - } - - target = xtables_find_target(name, XTF_TRY_LOAD); - if (target == NULL) - return; - - tginfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &len); - if (tginfo == NULL) - return; - - emu = calloc(1, sizeof(struct xt_entry_match) + len); - if (emu == NULL) - return; - - memcpy(emu->data, tginfo, len); - - if (target->alias) - printf("-j %s", target->alias(emu)); - else - printf("-j %s", target->name); - - /* FIXME missing parameter */ - if (target->save) - target->save(NULL, emu); - - free(emu); -} - -static void nft_immediate_save(struct nft_rule_expr *expr) -{ - uint32_t verdict; - - verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); - - switch(verdict) { - case NF_ACCEPT: - printf("-j ACCEPT"); - break; - case NF_DROP: - printf("-j DROP"); - break; - case NFT_RETURN: - printf("-j RETURN"); - break; - case NFT_GOTO: - printf("-g %s", - nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN)); - break; - case NFT_JUMP: - printf("-j %s", - nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN)); - break; - } -} - -static void -nft_print_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) -{ - uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); - uint32_t value; - const char *name; - char ifname[IFNAMSIZ]; - const char *ifname_ptr; - size_t len; - - e = nft_rule_expr_iter_next(iter); - if (e == NULL) - return; - - name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); - /* meta should be followed by cmp */ - if (strcmp(name, "cmp") != 0) { - DEBUGP("skipping no cmp after meta\n"); - return; - } - - switch(key) { - case NFT_META_IIF: - value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); - if_indextoname(value, ifname); - - switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) { - case NFT_CMP_EQ: - printf("-i %s ", ifname); - break; - case NFT_CMP_NEQ: - printf("! -i %s ", ifname); - break; - } - break; - case NFT_META_OIF: - value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); - if_indextoname(value, ifname); - - switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) { - case NFT_CMP_EQ: - printf("-o %s ", ifname); - break; - case NFT_CMP_NEQ: - printf("! -o %s ", ifname); - break; - } - break; - case NFT_META_IIFNAME: - ifname_ptr = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); - memcpy(ifname, ifname_ptr, len); - ifname[len] = '\0'; - - /* if this is zero, then assume this is a interface mask */ - if (if_nametoindex(ifname) == 0) { - ifname[len] = '+'; - ifname[len+1] = '\0'; - } - - switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) { - case NFT_CMP_EQ: - printf("-i %s ", ifname); - break; - case NFT_CMP_NEQ: - printf("! -i %s ", ifname); - break; - } - break; - case NFT_META_OIFNAME: - ifname_ptr = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); - memcpy(ifname, ifname_ptr, len); - ifname[len] = '\0'; - - /* if this is zero, then assume this is a interface mask */ - if (if_nametoindex(ifname) == 0) { - ifname[len] = '+'; - ifname[len+1] = '\0'; - } - - switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) { - case NFT_CMP_EQ: - printf("-o %s ", ifname); - break; - case NFT_CMP_NEQ: - printf("! -o %s ", ifname); - break; - } - break; - default: - DEBUGP("unknown meta key %d\n", key); - break; - } -} - -static void -nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - bool counters) -{ - if (counters) { - printf("-c %"PRIu64" %"PRIu64" ", - nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS), - nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES)); - } -} - void -nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters) +nft_rule_print_save(const struct iptables_command_state *cs, + struct nft_rule *r, enum nft_rule_print type, + unsigned int format) { - struct nft_rule_expr_iter *iter; - struct nft_rule_expr *expr; const char *chain = nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); + int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); + struct xtables_rule_match *matchp; + struct nft_family_ops *ops; + int ip_flags = 0; /* print chain name */ switch(type) { @@ -1011,34 +803,33 @@ nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters) break; } - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) - return; - - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + ops = nft_family_ops_lookup(family); + ip_flags = ops->save_firewall(cs, format); - if (strcmp(name, "counter") == 0) { - nft_print_counters(expr, iter, counters); - } else if (strcmp(name, "payload") == 0) { - struct nft_family_ops *ops = nft_family_ops_lookup( - nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)); - ops->print_payload(expr, iter); - } else if (strcmp(name, "meta") == 0) { - nft_print_meta(expr, iter); - } else if (strcmp(name, "match") == 0) { - nft_match_save(expr); - } else if (strcmp(name, "target") == 0) { - nft_target_save(expr); - } else if (strcmp(name, "immediate") == 0) { - nft_immediate_save(expr); - } + for (matchp = cs->matches; matchp; matchp = matchp->next) { + if (matchp->match->alias) { + printf("-m %s", + matchp->match->alias(matchp->match->m)); + } else + printf("-m %s", matchp->match->name); - expr = nft_rule_expr_iter_next(iter); + if (matchp->match->save != NULL) + matchp->match->save(NULL, matchp->match->m); + printf(" "); } + if (cs->target != NULL) { + if (cs->target->alias) { + printf("-j %s", cs->target->alias(cs->target->t)); + } else + printf("-j %s", cs->jumpto); + + if (cs->target->save != NULL) + cs->target->save(NULL, cs->target->t); + } else if (strlen(cs->jumpto) > 0) + printf("-%c %s", ip_flags & IPT_F_GOTO ? 'g' : 'j', + cs->jumpto); + printf("\n"); } @@ -1219,11 +1010,15 @@ int nft_rule_save(struct nft_handle *h, const char *table, bool counters) while (r != NULL) { const char *rule_table = nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE); + struct iptables_command_state cs = {}; if (strcmp(table, rule_table) != 0) goto next; - nft_rule_print_save(r, NFT_RULE_APPEND, counters); + nft_rule_to_iptables_command_state(r, &cs); + + nft_rule_print_save(&cs, r, NFT_RULE_APPEND, + counters ? 0 : FMT_NOCOUNTS); next: r = nft_rule_list_iter_next(iter); @@ -1661,187 +1456,58 @@ next: return 0; } -static int matches_howmany(struct xtables_rule_match *matches) -{ - struct xtables_rule_match *matchp; - int matches_ctr = 0; - - for (matchp = matches; matchp; matchp = matchp->next) - matches_ctr++; - - return matches_ctr; -} - static bool -__find_match(struct nft_rule_expr *expr, struct xtables_rule_match *matches) +compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2) { - const char *matchname = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); - /* Netlink aligns this match info, don't trust this length variable */ - const char *data = nft_rule_expr_get_str(expr, NFT_EXPR_MT_INFO); - struct xtables_rule_match *matchp; - bool found = false; + struct xtables_rule_match *mp1; + struct xtables_rule_match *mp2; - for (matchp = matches; matchp; matchp = matchp->next) { - struct xt_entry_match *m = matchp->match->m; + for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) { + struct xt_entry_match *m1 = mp1->match->m; + struct xt_entry_match *m2 = mp2->match->m; - if (strcmp(m->u.user.name, matchname) != 0) { + if (strcmp(m1->u.user.name, m2->u.user.name) != 0) { DEBUGP("mismatching match name\n"); - continue; + return false; } - if (memcmp(data, m->data, m->u.user.match_size - sizeof(*m)) != 0) { - DEBUGP("mismatch match data\n"); - continue; + if (m1->u.user.match_size != m2->u.user.match_size) { + DEBUGP("mismatching match size\n"); + return false; } - found = true; - break; - } - - return found; -} -static bool find_matches(struct xtables_rule_match *matches, struct nft_rule *r) -{ - struct nft_rule_expr_iter *iter; - struct nft_rule_expr *expr; - int kernel_matches = 0; - - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) - return false; - - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); - - if (strcmp(name, "match") == 0) { - if (!__find_match(expr, matches)) - return false; - - kernel_matches++; + if (memcmp(m1->data, m2->data, + m1->u.user.match_size - sizeof(*m1)) != 0) { + DEBUGP("mismatch match data\n"); + return false; } - expr = nft_rule_expr_iter_next(iter); } - nft_rule_expr_iter_destroy(iter); - /* same number of matches? */ - if (matches_howmany(matches) != kernel_matches) - return false; - - return true; -} - -static bool __find_target(struct nft_rule_expr *expr, struct xt_entry_target *t) -{ - size_t len; - const char *tgname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME); - /* Netlink aligns this target info, don't trust this length variable */ - const char *data = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &len); - - if (strcmp(t->u.user.name, tgname) != 0) { - DEBUGP("mismatching target name\n"); + /* Both cursors should be NULL */ + if (mp1 != mp2) { + DEBUGP("mismatch matches amount\n"); return false; } - if (memcmp(data, t->data, t->u.user.target_size - sizeof(*t)) != 0) - return false; - return true; } -static int targets_howmany(struct xtables_target *target) -{ - return target != NULL ? 1 : 0; -} - static bool -find_target(struct xtables_target *target, struct nft_rule *r) +compare_targets(struct xtables_target *tg1, struct xtables_target *tg2) { - struct nft_rule_expr_iter *iter; - struct nft_rule_expr *expr; - int kernel_targets = 0; - - /* Special case: we use native immediate expressions to emulated - * standard targets. Also, we don't want to crash with no targets. - */ - if (target == NULL || strcmp(target->name, "standard") == 0) + if (tg1 == NULL && tg2 == NULL) return true; - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) + if ((tg1 == NULL && tg2 != NULL) || (tg1 != NULL && tg2 == NULL)) return false; - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); - - if (strcmp(name, "target") == 0) { - /* we may support several targets in the future */ - if (!__find_target(expr, target->t)) - return false; - - kernel_targets++; - } - expr = nft_rule_expr_iter_next(iter); - } - nft_rule_expr_iter_destroy(iter); - - /* same number of targets? */ - if (targets_howmany(target) != kernel_targets) { - DEBUGP("kernel targets is %d but we passed %d\n", - kernel_targets, targets_howmany(target)); + if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0) return false; - } - - return true; -} - -static bool -find_immediate(struct nft_rule *r, const char *jumpto) -{ - struct nft_rule_expr_iter *iter; - struct nft_rule_expr *expr; - iter = nft_rule_expr_iter_create(r); - if (iter == NULL) + if (memcmp(tg1->t->data, tg2->t->data, + tg1->t->u.user.target_size - sizeof(*tg1->t)) != 0) { return false; - - expr = nft_rule_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); - - if (strcmp(name, "immediate") == 0) { - int verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); - const char *verdict_name = NULL; - - /* No target specified but immediate shows up, this - * is not the rule we are looking for. - */ - if (strlen(jumpto) == 0) - return false; - - switch(verdict) { - case NF_ACCEPT: - verdict_name = "ACCEPT"; - break; - case NF_DROP: - verdict_name = "DROP"; - break; - case NFT_RETURN: - verdict_name = "RETURN"; - break; - } - - /* Standard target? */ - if (verdict_name && strcmp(jumpto, verdict_name) != 0) - return false; - } - expr = nft_rule_expr_iter_next(iter); } - nft_rule_expr_iter_destroy(iter); return true; } @@ -1911,28 +1577,27 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, break; } else { /* Delete by matching rule case */ + nft_rule_to_iptables_command_state(r, &this); + DEBUGP("comparing with... "); #ifdef DEBUG_DEL - nft_rule_print_save(r, NFT_RULE_APPEND, 0); + nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0); #endif - - nft_rule_to_iptables_command_state(r, &this); - if (!ops->is_same(cs, &this)) goto next; - if (!find_matches(cs->matches, r)) { - DEBUGP("matches not found\n"); + if (!compare_matches(cs->matches, this.matches)) { + DEBUGP("Different matches\n"); goto next; } - if (!find_target(cs->target, r)) { - DEBUGP("target not found\n"); + if (!compare_targets(cs->target, this.target)) { + DEBUGP("Different target\n"); goto next; } - if (!find_immediate(r, cs->jumpto)) { - DEBUGP("immediate not found\n"); + if (strcmp(cs->jumpto, this.jumpto) != 0) { + DEBUGP("Different verdict\n"); goto next; } @@ -2324,7 +1989,11 @@ err: static void list_save(struct nft_rule *r, unsigned int num, unsigned int format) { - nft_rule_print_save(r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS)); + struct iptables_command_state cs = {}; + + nft_rule_to_iptables_command_state(r, &cs); + + nft_rule_print_save(&cs, r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS)); } static int diff --git a/iptables/nft.h b/iptables/nft.h index f3317c92..006c0318 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -87,7 +87,9 @@ enum nft_rule_print { NFT_RULE_DEL, }; -void nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters); +void nft_rule_print_save(const struct iptables_command_state *cs, + struct nft_rule *r, enum nft_rule_print type, + unsigned int format); /* * global commit and abort diff --git a/iptables/xtables-events.c b/iptables/xtables-events.c index 64ae9726..20392a5b 100644 --- a/iptables/xtables-events.c +++ b/iptables/xtables-events.c @@ -58,6 +58,7 @@ static bool counters; static int rule_cb(const struct nlmsghdr *nlh, int type) { + struct iptables_command_state cs = {}; struct nft_rule *r; r = nft_rule_alloc(); @@ -71,6 +72,8 @@ static int rule_cb(const struct nlmsghdr *nlh, int type) goto err_free; } + nft_rule_to_iptables_command_state(r, &cs); + switch(nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)) { case AF_INET: printf("-4 "); @@ -82,9 +85,11 @@ static int rule_cb(const struct nlmsghdr *nlh, int type) break; } - nft_rule_print_save(r, type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND : - NFT_RULE_DEL, - counters); + + nft_rule_print_save(&cs, r, + type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND : + NFT_RULE_DEL, + counters ? 0 : FMT_NOCOUNTS); err_free: nft_rule_free(r); err: -- cgit v1.2.3 From eb4b65c49994e44e6ad617fe3f60c063d0c331c4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 20 Aug 2013 20:24:26 +0200 Subject: nft: fix wrong flags handling in print_firewall_details Unfortunately, IPT_F_* and IP6T_F_* don't overlap, therefore, we have to add an specific function to print the fragment flag, otherwise xtables -6 misinterprets the protocol flag, ie. Chain INPUT (policy ACCEPT) tcp -f ::/0 ::/0 Note that -f should not show up. This problem was likely added with the IPv6 support for the compatibility layer. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 17 +++++++++++-- iptables/nft-ipv6.c | 4 +-- iptables/nft-shared.c | 67 ++++++++++++++++++++++++++------------------------- iptables/nft-shared.h | 3 ++- 4 files changed, 53 insertions(+), 38 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 08a8c653..a08df713 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -231,6 +231,18 @@ static void print_ipv4_addr(const struct iptables_command_state *cs, } } +static void print_fragment(unsigned int flags, unsigned int invflags, + unsigned int format) +{ + if (!(format & FMT_OPTIONS)) + return; + + if (format & FMT_NOTABLE) + fputs("opt ", stdout); + fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); + fputc(' ', stdout); +} static void nft_ipv4_print_firewall(struct nft_rule *r, unsigned int num, unsigned int format) @@ -241,9 +253,10 @@ static void nft_ipv4_print_firewall(struct nft_rule *r, unsigned int num, print_firewall_details(&cs, cs.jumpto, cs.fw.ip.flags, cs.fw.ip.invflags, cs.fw.ip.proto, - cs.fw.ip.iniface, cs.fw.ip.outiface, num, format); - + print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format); + print_ifaces(cs.fw.ip.iniface, cs.fw.ip.outiface, cs.fw.ip.invflags, + format); print_ipv4_addr(&cs, format); if (format & FMT_NOTABLE) diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 91ef255c..9bb57981 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -161,9 +161,9 @@ static void nft_ipv6_print_firewall(struct nft_rule *r, unsigned int num, print_firewall_details(&cs, cs.jumpto, cs.fw6.ipv6.flags, cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto, - cs.fw6.ipv6.iniface, cs.fw6.ipv6.outiface, num, format); - + print_ifaces(cs.fw6.ipv6.iniface, cs.fw6.ipv6.outiface, + cs.fw6.ipv6.invflags, format); print_ipv6_addr(&cs, format); if (format & FMT_NOTABLE) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index ad5e80ea..e981c0d4 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -516,7 +516,6 @@ void print_num(uint64_t number, unsigned int format) void print_firewall_details(const struct iptables_command_state *cs, const char *targname, uint8_t flags, uint8_t invflags, uint8_t proto, - const char *iniface, const char *outiface, unsigned int num, unsigned int format) { if (format & FMT_LINENUMBERS) @@ -539,43 +538,45 @@ void print_firewall_details(const struct iptables_command_state *cs, else printf(FMT("%-5hu", "%hu "), proto); } +} - if (format & FMT_OPTIONS) { - if (format & FMT_NOTABLE) - fputs("opt ", stdout); - fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); - fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); - fputc(' ', stdout); - } +void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags, + unsigned int format) +{ + char iface[IFNAMSIZ+2]; - if (format & FMT_VIA) { - char iface[IFNAMSIZ+2]; - if (invflags & IPT_INV_VIA_IN) { - iface[0] = '!'; - iface[1] = '\0'; - } - else iface[0] = '\0'; + if (!(format & FMT_VIA)) + return; - if (iniface[0] != '\0') { - strcat(iface, iniface); - } - else if (format & FMT_NUMERIC) strcat(iface, "*"); - else strcat(iface, "any"); - printf(FMT(" %-6s ","in %s "), iface); + if (invflags & IPT_INV_VIA_IN) { + iface[0] = '!'; + iface[1] = '\0'; + } else + iface[0] = '\0'; - if (invflags & IPT_INV_VIA_OUT) { - iface[0] = '!'; - iface[1] = '\0'; - } - else iface[0] = '\0'; + if (iniface[0] != '\0') + strcat(iface, iniface); + else if (format & FMT_NUMERIC) + strcat(iface, "*"); + else + strcat(iface, "any"); - if (outiface[0] != '\0') { - strcat(iface, outiface); - } - else if (format & FMT_NUMERIC) strcat(iface, "*"); - else strcat(iface, "any"); - printf(FMT("%-6s ","out %s "), iface); - } + printf(FMT(" %-6s ","in %s "), iface); + + if (invflags & IPT_INV_VIA_OUT) { + iface[0] = '!'; + iface[1] = '\0'; + } else + iface[0] = '\0'; + + if (outiface[0] != '\0') + strcat(iface, outiface); + else if (format & FMT_NUMERIC) + strcat(iface, "*"); + else + strcat(iface, "any"); + + printf(FMT("%-6s ","out %s "), iface); } static void diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index e77b303d..6e45538c 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -90,8 +90,9 @@ void print_num(uint64_t number, unsigned int format); void print_firewall_details(const struct iptables_command_state *cs, const char *targname, uint8_t flags, uint8_t invflags, uint8_t proto, - const char *iniface, const char *outiface, unsigned int num, unsigned int format); +void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags, + unsigned int format); void print_matches_and_target(struct iptables_command_state *cs, unsigned int format); void save_firewall_details(const struct iptables_command_state *cs, -- cgit v1.2.3 From 36cba824e1689c6255d4e33b7fa82541a774609b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 20 Aug 2013 20:42:50 +0200 Subject: nft: use xtables_print_num So we can kill our own implementation. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 32 ++++---------------------------- iptables/nft-shared.h | 1 - iptables/nft.c | 4 ++-- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index e981c0d4..9e57b361 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -489,30 +489,6 @@ void nft_rule_to_iptables_command_state(struct nft_rule *r, cs->jumpto = ""; } -void print_num(uint64_t number, unsigned int format) -{ - if (format & FMT_KILOMEGAGIGA) { - if (number > 99999) { - number = (number + 500) / 1000; - if (number > 9999) { - number = (number + 500) / 1000; - if (number > 9999) { - number = (number + 500) / 1000; - if (number > 9999) { - number = (number + 500) / 1000; - printf(FMT("%4lluT ","%lluT "), (unsigned long long)number); - } - else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number); - } - else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number); - } else - printf(FMT("%4lluK ","%lluK "), (unsigned long long)number); - } else - printf(FMT("%5llu ","%llu "), (unsigned long long)number); - } else - printf(FMT("%8llu ","%llu "), (unsigned long long)number); -} - void print_firewall_details(const struct iptables_command_state *cs, const char *targname, uint8_t flags, uint8_t invflags, uint8_t proto, @@ -522,8 +498,8 @@ void print_firewall_details(const struct iptables_command_state *cs, printf(FMT("%-4u ", "%u "), num); if (!(format & FMT_NOCOUNTS)) { - print_num(cs->counters.pcnt, format); - print_num(cs->counters.bcnt, format); + xtables_print_num(cs->counters.pcnt, format); + xtables_print_num(cs->counters.bcnt, format); } if (!(format & FMT_NOTARGET)) @@ -613,8 +589,8 @@ void save_firewall_details(const struct iptables_command_state *cs, { if (!(format & FMT_NOCOUNTS)) { printf("-c "); - print_num(cs->counters.pcnt, format); - print_num(cs->counters.bcnt, format); + xtables_print_num(cs->counters.pcnt, format); + xtables_print_num(cs->counters.bcnt, format); } if (iniface != NULL) { diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 6e45538c..861b6db2 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -86,7 +86,6 @@ void get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv); void nft_rule_to_iptables_command_state(struct nft_rule *r, struct iptables_command_state *cs); -void print_num(uint64_t number, unsigned int format); void print_firewall_details(const struct iptables_command_state *cs, const char *targname, uint8_t flags, uint8_t invflags, uint8_t proto, diff --git a/iptables/nft.c b/iptables/nft.c index 3b393cc7..5c79455f 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1817,9 +1817,9 @@ print_header(unsigned int format, const char *chain, const char *pol, printf(" (policy %s", pol); if (!(format & FMT_NOCOUNTS)) { fputc(' ', stdout); - print_num(counters->pcnt, (format|FMT_NOTABLE)); + xtables_print_num(counters->pcnt, (format|FMT_NOTABLE)); fputs("packets, ", stdout); - print_num(counters->bcnt, (format|FMT_NOTABLE)); + xtables_print_num(counters->bcnt, (format|FMT_NOTABLE)); fputs("bytes", stdout); } printf(")\n"); -- cgit v1.2.3 From a69cc575295eedb44f0fa33cd5fcf1cc0114133a Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Mon, 19 Aug 2013 15:04:06 +0300 Subject: xtables: allow to reset the counters of an existing rule Now that we convert nft rules to native xt command structure, it's easier to reset the counters by replacing the existing rule by a new one with all counters set to zero. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 35 +++++++++++++++++++++++++++++++++++ iptables/nft.h | 1 + iptables/xtables.c | 15 +++++++-------- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 5c79455f..143293b5 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2091,6 +2091,41 @@ err: return ret; } +int nft_rule_zero_counters(struct nft_handle *h, const char *chain, + const char *table, int rulenum) +{ + struct iptables_command_state cs = {}; + struct nft_rule_list *list; + struct nft_rule *r; + int ret = 0; + + nft_fn = nft_rule_delete; + + list = nft_rule_list_create(h); + if (list == NULL) + return 0; + + r = nft_rule_find(list, chain, table, NULL, rulenum); + if (r == NULL) { + errno = ENOENT; + ret = 1; + goto error; + } + + nft_rule_to_iptables_command_state(r, &cs); + + cs.counters.pcnt = cs.counters.bcnt = 0; + + ret = nft_rule_append(h, chain, table, &cs, + nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE), + false); + +error: + nft_rule_list_destroy(list); + + return ret; +} + static int nft_action(struct nft_handle *h, int type) { char buf[MNL_SOCKET_BUFFER_SIZE]; diff --git a/iptables/nft.h b/iptables/nft.h index 006c0318..fe1b9c81 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -81,6 +81,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, in int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *table, int rulenum, int counters); int nft_rule_save(struct nft_handle *h, const char *table, bool counters); int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table); +int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *table, int rulenum); enum nft_rule_print { NFT_RULE_APPEND, diff --git a/iptables/xtables.c b/iptables/xtables.c index 3e6092f2..279b77bf 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -1173,8 +1173,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) ret = nft_chain_zero_counters(h, chain, *table); break; case CMD_ZERO_NUM: - /* FIXME */ -// ret = iptc_zero_counter(chain, rulenum, *handle); + ret = nft_rule_zero_counters(h, chain, *table, rulenum - 1); break; case CMD_LIST: case CMD_LIST|CMD_ZERO: @@ -1187,9 +1186,9 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) cs.options&OPT_LINENUMBERS); if (ret && (command & CMD_ZERO)) ret = nft_chain_zero_counters(h, chain, *table); - /* FIXME */ -/* if (ret && (command & CMD_ZERO_NUM)) - ret = iptc_zero_counter(chain, rulenum, *handle); */ + if (ret && (command & CMD_ZERO_NUM)) + ret = nft_rule_zero_counters(h, chain, *table, + rulenum - 1); break; case CMD_LIST_RULES: case CMD_LIST_RULES|CMD_ZERO: @@ -1197,9 +1196,9 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) ret = list_rules(h, chain, *table, rulenum, cs.options&OPT_VERBOSE); if (ret && (command & CMD_ZERO)) ret = nft_chain_zero_counters(h, chain, *table); - /* FIXME */ -/* if (ret && (command & CMD_ZERO_NUM)) - ret = iptc_zero_counter(chain, rulenum, *handle); */ + if (ret && (command & CMD_ZERO_NUM)) + ret = nft_rule_zero_counters(h, chain, *table, + rulenum - 1); break; case CMD_NEW_CHAIN: ret = nft_chain_user_add(h, chain, *table); -- cgit v1.2.3 From 04d9ad94a40e795dfa8d4cfd0bf3f092d60ecc47 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Wed, 4 Sep 2013 14:36:41 +0200 Subject: nft: refactoring parse operations for more genericity This allows to reuse the nft_parse_* function for the bridge and arp family (not yet supported). Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 27 ++++++++++++++++++++++----- iptables/nft-ipv6.c | 26 +++++++++++++++++++++----- iptables/nft-shared.c | 43 +++++++++++++++++++++++++------------------ iptables/nft-shared.h | 8 ++++---- 4 files changed, 72 insertions(+), 32 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index a08df713..b7a60952 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -148,17 +148,20 @@ static const char *mask_to_str(uint32_t mask) } static void nft_ipv4_parse_meta(struct nft_rule_expr *e, uint8_t key, - struct iptables_command_state *cs) + void *data) { + struct iptables_command_state *cs = data; + parse_meta(e, key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask, cs->fw.ip.outiface, cs->fw.ip.outiface_mask, &cs->fw.ip.invflags); } static void nft_ipv4_parse_payload(struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs, - uint32_t offset) + uint32_t offset, void *data) { + struct iptables_command_state *cs = data; + switch(offset) { struct in_addr addr; uint8_t proto; @@ -196,9 +199,15 @@ static void nft_ipv4_parse_payload(struct nft_rule_expr_iter *iter, } } -static void nft_ipv4_parse_immediate(struct iptables_command_state *cs) +static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto, + void *data) { - cs->fw.ip.flags |= IPT_F_GOTO; + struct iptables_command_state *cs = data; + + cs->jumpto = jumpto; + + if (nft_goto) + cs->fw.ip.flags |= IPT_F_GOTO; } static void print_ipv4_addr(const struct iptables_command_state *cs, @@ -351,6 +360,13 @@ static void nft_ipv4_post_parse(int command, " source or destination IP addresses"); } +static void nft_ipv4_parse_target(struct xtables_target *t, void *data) +{ + struct iptables_command_state *cs = data; + + cs->target = t; +} + struct nft_family_ops nft_family_ops_ipv4 = { .add = nft_ipv4_add, .is_same = nft_ipv4_is_same, @@ -360,4 +376,5 @@ struct nft_family_ops nft_family_ops_ipv4 = { .print_firewall = nft_ipv4_print_firewall, .save_firewall = nft_ipv4_save_firewall, .post_parse = nft_ipv4_post_parse, + .parse_target = nft_ipv4_parse_target, }; diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 9bb57981..27e63a45 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -70,17 +70,19 @@ static bool nft_ipv6_is_same(const struct iptables_command_state *a, } static void nft_ipv6_parse_meta(struct nft_rule_expr *e, uint8_t key, - struct iptables_command_state *cs) + void *data) { + struct iptables_command_state *cs = data; + parse_meta(e, key, cs->fw6.ipv6.iniface, cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface, cs->fw6.ipv6.outiface_mask, &cs->fw6.ipv6.invflags); } static void nft_ipv6_parse_payload(struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs, - uint32_t offset) + uint32_t offset, void *data) { + struct iptables_command_state *cs = data; switch (offset) { struct in6_addr addr; uint8_t proto; @@ -110,9 +112,15 @@ static void nft_ipv6_parse_payload(struct nft_rule_expr_iter *iter, } } -static void nft_ipv6_parse_immediate(struct iptables_command_state *cs) +static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto, + void *data) { - cs->fw6.ipv6.flags |= IPT_F_GOTO; + struct iptables_command_state *cs = data; + + cs->jumpto = jumpto; + + if (nft_goto) + cs->fw6.ipv6.flags |= IPT_F_GOTO; } static void print_ipv6_addr(const struct iptables_command_state *cs, @@ -274,6 +282,13 @@ static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs, " source or destination IP addresses"); } +static void nft_ipv6_parse_target(struct xtables_target *t, void *data) +{ + struct iptables_command_state *cs = data; + + cs->target = t; +} + struct nft_family_ops nft_family_ops_ipv6 = { .add = nft_ipv6_add, .is_same = nft_ipv6_is_same, @@ -283,4 +298,5 @@ struct nft_family_ops nft_family_ops_ipv6 = { .print_firewall = nft_ipv6_print_firewall, .save_firewall = nft_ipv6_save_firewall, .post_parse = nft_ipv6_post_parse, + .parse_target = nft_ipv6_parse_target, }; diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 9e57b361..cdc3f835 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -283,13 +283,14 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, static void nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) + int family, void *data) { size_t tg_len; const char *targname = nft_rule_expr_get_str(e, NFT_EXPR_TG_NAME); const void *targinfo = nft_rule_expr_get(e, NFT_EXPR_TG_INFO, &tg_len); struct xtables_target *target; struct xt_entry_target *t; + struct nft_family_ops *ops; target = xtables_find_target(targname, XTF_TRY_LOAD); if (target == NULL) @@ -306,7 +307,9 @@ nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, strcpy(t->u.user.name, target->name); target->t = t; - cs->target = target; + + ops = nft_family_ops_lookup(family); + ops->parse_target(target, data); } static void @@ -380,7 +383,7 @@ void get_cmp_data(struct nft_rule_expr_iter *iter, static void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - int family, struct iptables_command_state *cs) + int family, void *data) { uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); struct nft_family_ops *ops = nft_family_ops_lookup(family); @@ -396,19 +399,19 @@ nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, return; } - ops->parse_meta(e, key, cs); + ops->parse_meta(e, key, data); } static void nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - int family, struct iptables_command_state *cs) + int family, void *data) { struct nft_family_ops *ops = nft_family_ops_lookup(family); uint32_t offset; offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - ops->parse_payload(iter, cs, offset); + ops->parse_payload(iter, offset, data); } static void @@ -421,30 +424,34 @@ nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, static void nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - int family, struct iptables_command_state *cs) + int family, void *data) { int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); struct nft_family_ops *ops; + const char *jumpto; + bool nft_goto = false; /* Standard target? */ switch(verdict) { case NF_ACCEPT: - cs->jumpto = "ACCEPT"; - return; + jumpto = "ACCEPT"; + break; case NF_DROP: - cs->jumpto = "DROP"; - return; + jumpto = "DROP"; + break; case NFT_RETURN: - cs->jumpto = "RETURN"; - return; + jumpto = "RETURN"; + break;; case NFT_GOTO: - ops = nft_family_ops_lookup(family); - ops->parse_immediate(cs); + nft_goto = true; case NFT_JUMP: - cs->jumpto = chain; - return; + jumpto = chain; + break; } + + ops = nft_family_ops_lookup(family); + ops->parse_immediate(jumpto, nft_goto, data); } void nft_rule_to_iptables_command_state(struct nft_rule *r, @@ -474,7 +481,7 @@ void nft_rule_to_iptables_command_state(struct nft_rule *r, else if (strcmp(name, "match") == 0) nft_parse_match(expr, iter, cs); else if (strcmp(name, "target") == 0) - nft_parse_target(expr, iter, cs); + nft_parse_target(expr, iter, family, cs); expr = nft_rule_expr_iter_next(iter); } diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 861b6db2..ed2617cb 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -43,17 +43,17 @@ struct nft_family_ops { void (*print_payload)(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter); void (*parse_meta)(struct nft_rule_expr *e, uint8_t key, - struct iptables_command_state *cs); + void *data); void (*parse_payload)(struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs, - uint32_t offset); - void (*parse_immediate)(struct iptables_command_state *cs); + uint32_t offset, void *data); + void (*parse_immediate)(const char *jumpto, bool nft_goto, void *data); void (*print_firewall)(struct nft_rule *r, unsigned int num, unsigned int format); uint8_t (*save_firewall)(const struct iptables_command_state *cs, unsigned int format); void (*post_parse)(int command, struct iptables_command_state *cs, struct xtables_args *args); + void (*parse_target)(struct xtables_target *t, void *data); }; void add_meta(struct nft_rule *r, uint32_t key); -- cgit v1.2.3 From 7791905f7db3bce63d3316c5adaf2f735cff3c1d Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Fri, 6 Sep 2013 10:51:48 +0300 Subject: nft: Fix a minor compilation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nft-shared.c: In function ‘nft_rule_to_iptables_command_state’: nft-shared.c:454:22: warning: ‘jumpto’ may be used uninitialized in this function [-Wmaybe-uninitialized] nft-shared.c:432:14: note: ‘jumpto’ was declared here All verdicts are managed and jumpto has to get a value, but since the compiler complains, let's fix it. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index cdc3f835..5681e264 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -429,7 +429,7 @@ nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); struct nft_family_ops *ops; - const char *jumpto; + const char *jumpto = NULL; bool nft_goto = false; /* Standard target? */ -- cgit v1.2.3 From e83e35e236a33dfdf3e401adb7d7e18362cf1961 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 8 Sep 2013 23:53:05 +0200 Subject: nft: generalize rule addition family hook This should help Giuseppe with his ARP support works, this change was missing in (618309c nft: refactoring parse operations for more genericity). Based on patch from Giuseppe. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 3 ++- iptables/nft-ipv6.c | 4 +++- iptables/nft-shared.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index b7a60952..40340984 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -24,8 +24,9 @@ #include "nft-shared.h" -static int nft_ipv4_add(struct nft_rule *r, struct iptables_command_state *cs) +static int nft_ipv4_add(struct nft_rule *r, void *data) { + struct iptables_command_state *cs = data; uint32_t op; if (cs->fw.ip.iniface[0] != '\0') diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 27e63a45..2efe95e3 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -22,8 +22,10 @@ #include "nft-shared.h" -static int nft_ipv6_add(struct nft_rule *r, struct iptables_command_state *cs) +static int nft_ipv6_add(struct nft_rule *r, void *data) { + struct iptables_command_state *cs = data; + if (cs->fw6.ipv6.iniface[0] != '\0') add_iniface(r, cs->fw6.ipv6.iniface, cs->fw6.ipv6.invflags); diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index ed2617cb..3f1a9a4a 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -37,7 +37,7 @@ struct xtables_args; struct nft_family_ops { - int (*add)(struct nft_rule *r, struct iptables_command_state *cs); + int (*add)(struct nft_rule *r, void *data); bool (*is_same)(const struct iptables_command_state *a, const struct iptables_command_state *b); void (*print_payload)(struct nft_rule_expr *e, -- cgit v1.2.3 From 67da6075a4e7ced0e8cc452d73ce8ab06cbf8cd9 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Fri, 13 Sep 2013 18:25:57 +0200 Subject: nft: skip unset tables on table configuration emulation The ARP family has less tables, so skip iteration once we find a null one. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index 143293b5..49d345d2 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -295,6 +295,9 @@ static struct builtin_table bool found = false; for (i=0; itables[i].name == NULL) + break; + if (strcmp(h->tables[i].name, table) != 0) continue; -- cgit v1.2.3 From 84909d171585d77fe769f03e2b1b96eab0aa0213 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Mon, 9 Sep 2013 12:54:04 +0200 Subject: xtables: bootstrap ARP compatibility layer for nftables This patch bootstraps ARP support for the compatibility layer: 1) copy original arptables code into xtables-arp.c 2) adapt it to fit into the existing nft infrastructure. 3) add the builtin table/chains for ARP. 4) add necessary parts so xtables-multi can provide xtables-arp. 5) add basic support for rule addition (-A), insertion (-I) and listing (-L). [ This was originally posted in a series of patches with interdependencies that I have collapsed to leave the repository in consistent state. This patch includes the following changes I made: * Rename from xtables-arptables to xtables-arp, previous name too long. * Remove nft-arptables.c, now we have one single nft-arp.c file. Moved specific ARP functions to nft.c. Those should go away at some point as some refactorization should allow to accomodate those functions to the existing infrastructure. * Fix --opcode Request/Reply, so we can do something useful with this like dropping ARP request/replies. --pablo ] Signed-off-by: Giuseppe Longo Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- etc/xtables.conf | 8 + iptables/Makefile.am | 7 +- iptables/nft-arp.c | 549 ++++++++++++++ iptables/nft-shared.c | 14 +- iptables/nft-shared.h | 30 + iptables/nft.c | 303 +++++++- iptables/nft.h | 35 + iptables/xtables-arp-standalone.c | 86 +++ iptables/xtables-arp.c | 1457 +++++++++++++++++++++++++++++++++++++ iptables/xtables-multi.c | 1 + iptables/xtables-multi.h | 1 + 11 files changed, 2466 insertions(+), 25 deletions(-) create mode 100644 iptables/nft-arp.c create mode 100644 iptables/xtables-arp-standalone.c create mode 100644 iptables/xtables-arp.c diff --git a/etc/xtables.conf b/etc/xtables.conf index 7b2b8e5f..d37b0d7c 100644 --- a/etc/xtables.conf +++ b/etc/xtables.conf @@ -65,3 +65,11 @@ family ipv6 { chain OUTPUT hook NF_INET_LOCAL_OUT prio 50 } } + +family arp { + table filter { + chain INPUT hook NF_ARP_IN prio 0 + chain FORWARD hook NF_ARP_FORWARD prio 0 + chain OUTPUT hook NF_ARP_OUT prio 0 + } +} \ No newline at end of file diff --git a/iptables/Makefile.am b/iptables/Makefile.am index 5cbd8ff9..b9a9743e 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -31,8 +31,9 @@ if HAVE_LIBNFTABLES xtables_multi_SOURCES += xtables-config-parser.y xtables-config-syntax.l xtables_multi_SOURCES += xtables-save.c xtables-restore.c \ xtables-standalone.c xtables.c nft.c \ - nft-shared.c nft-ipv4.c nft-ipv6.c \ - xtables-config.c xtables-events.c + nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \ + xtables-config.c xtables-events.c \ + xtables-arp-standalone.c xtables-arp.c xtables_multi_LDADD += -lmnl -lnftables ${libmnl_LIBS} ${libnftables_LIBS} xtables_multi_CFLAGS += -DENABLE_NFTABLES # yacc and lex generate dirty code @@ -57,7 +58,7 @@ v6_sbin_links = ip6tables ip6tables-restore ip6tables-save endif if ENABLE_NFTABLES x_sbin_links = xtables xtables-restore xtables-save xtables-config \ - xtables-events + xtables-events xtables-arp endif iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c new file mode 100644 index 00000000..09b939d0 --- /dev/null +++ b/iptables/nft-arp.c @@ -0,0 +1,549 @@ +/* + * (C) 2013 by Giuseppe Longo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "nft-shared.h" + +/* a few names */ +char *opcodes[] = +{ + "Request", + "Reply", + "Request_Reverse", + "Reply_Reverse", + "DRARP_Request", + "DRARP_Reply", + "DRARP_Error", + "InARP_Request", + "ARP_NAK", +}; + +static char * +addr_to_dotted(const struct in_addr *addrp) +{ + static char buf[20]; + const unsigned char *bytep; + + bytep = (const unsigned char *) &(addrp->s_addr); + sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); + return buf; +} + +static char * +addr_to_host(const struct in_addr *addr) +{ + struct hostent *host; + + if ((host = gethostbyaddr((char *) addr, + sizeof(struct in_addr), AF_INET)) != NULL) + return (char *) host->h_name; + + return (char *) NULL; +} + +static char * +addr_to_network(const struct in_addr *addr) +{ + struct netent *net; + + if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) + return (char *) net->n_name; + + return (char *) NULL; +} + +static char * +addr_to_anyname(const struct in_addr *addr) +{ + char *name; + + if ((name = addr_to_host(addr)) != NULL || + (name = addr_to_network(addr)) != NULL) + return name; + + return addr_to_dotted(addr); +} + +static char * +mask_to_dotted(const struct in_addr *mask) +{ + int i; + static char buf[20]; + u_int32_t maskaddr, bits; + + maskaddr = ntohl(mask->s_addr); + + if (maskaddr == 0xFFFFFFFFL) + /* we don't want to see "/32" */ + return ""; + + i = 32; + bits = 0xFFFFFFFEL; + while (--i >= 0 && maskaddr != bits) + bits <<= 1; + if (i >= 0) + sprintf(buf, "/%d", i); + else + /* mask was not a decent combination of 1's and 0's */ + sprintf(buf, "/%s", addr_to_dotted(mask)); + + return buf; +} + +static void print_mac(const unsigned char *mac, int l) +{ + int j; + + for (j = 0; j < l; j++) + printf("%02x%s", mac[j], + (j==l-1) ? "" : ":"); +} + +static void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l) +{ + int i; + + print_mac(mac, l); + for (i = 0; i < l ; i++) + if (mask[i] != 255) + break; + if (i == l) + return; + printf("/"); + print_mac(mask, l); +} + +static uint8_t arpt_to_ipt_flags(uint16_t invflags) +{ + uint8_t result = 0; + + if (invflags & ARPT_INV_VIA_IN) + result |= IPT_INV_VIA_IN; + + if (invflags & ARPT_INV_VIA_OUT) + result |= IPT_INV_VIA_OUT; + + if (invflags & ARPT_INV_SRCIP) + result |= IPT_INV_SRCIP; + + if (invflags & ARPT_INV_TGTIP) + result |= IPT_INV_DSTIP; + + if (invflags & ARPT_INV_ARPPRO) + result |= IPT_INV_PROTO; + + if (invflags & ARPT_INV_MASK) + result |= IPT_INV_MASK; + + return result; +} + +static int nft_arp_add(struct nft_rule *r, void *data) +{ + struct arpt_entry *fw = data; + uint8_t flags = arpt_to_ipt_flags(fw->arp.invflags); + + if (fw->arp.iniface[0] != '\0') + add_iniface(r, fw->arp.iniface, flags); + + if (fw->arp.outiface[0] != '\0') + add_outiface(r, fw->arp.outiface, flags); + + if (fw->arp.arhrd != 0) { + add_payload(r, offsetof(struct arphdr, ar_hrd), 2); + add_cmp_u16(r, fw->arp.arhrd, NFT_CMP_EQ); + } + + if (fw->arp.arpro != 0) { + add_payload(r, offsetof(struct arphdr, ar_pro), 2); + add_cmp_u16(r, fw->arp.arpro, NFT_CMP_EQ); + } + + if (fw->arp.arhln != 0) + add_proto(r, offsetof(struct arphdr, ar_hln), 1, + fw->arp.arhln, flags); + + add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, 0); + + if (fw->arp.arpop != 0) { + add_payload(r, offsetof(struct arphdr, ar_op), 2); + add_cmp_u16(r, fw->arp.arpop, NFT_CMP_EQ); + } + + if (fw->arp.src_devaddr.addr[0] != '\0') { + add_payload(r, sizeof(struct arphdr), fw->arp.arhln); + add_cmp_ptr(r, NFT_CMP_EQ, fw->arp.src_devaddr.addr, fw->arp.arhln); + } + + if (fw->arp.src.s_addr != 0) + add_addr(r, sizeof(struct arphdr) + fw->arp.arhln, + &fw->arp.src.s_addr, 4, flags); + + if (fw->arp.tgt_devaddr.addr[0] != '\0') { + add_payload(r, sizeof(struct arphdr) + fw->arp.arhln + 4, fw->arp.arhln); + add_cmp_ptr(r, NFT_CMP_EQ, fw->arp.tgt_devaddr.addr, fw->arp.arhln); + } + + if (fw->arp.tgt.s_addr != 0) + add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr), + &fw->arp.tgt.s_addr, 4, flags); + + return 0; +} + +static uint16_t ipt_to_arpt_flags(uint8_t invflags) +{ + uint16_t result = 0; + + if (invflags & IPT_INV_VIA_IN) + result |= ARPT_INV_VIA_IN; + + if (invflags & IPT_INV_VIA_OUT) + result |= ARPT_INV_VIA_OUT; + + if (invflags & IPT_INV_SRCIP) + result |= ARPT_INV_SRCIP; + + if (invflags & IPT_INV_DSTIP) + result |= ARPT_INV_TGTIP; + + if (invflags & IPT_INV_PROTO) + result |= ARPT_INV_ARPPRO; + + if (invflags & IPT_INV_MASK) + result |= ARPT_INV_MASK; + + return result; +} + +static void nft_arp_parse_meta(struct nft_rule_expr *e, uint8_t key, + void *data) +{ + struct arpt_entry *fw = data; + uint8_t flags; + + parse_meta(e, key, fw->arp.iniface, fw->arp.iniface_mask, + fw->arp.outiface, fw->arp.outiface_mask, + &flags); + + fw->arp.invflags |= ipt_to_arpt_flags(flags); +} + +static void nft_arp_parse_target(struct xtables_target *t, void *data) +{ + struct arpt_entry *fw = data; + size_t size = sizeof(struct arpt_entry); + struct xt_entry_target **target; + + fw->target_offset = size; + fw->next_offset = size + t->t->u.target_size; + + target = (void *) fw + fw->target_offset; + *target = t->t; +} + +static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto, + void *data) +{ + struct xtables_target *target; + size_t size; + + target = xtables_find_target(XT_STANDARD_TARGET, + XTF_LOAD_MUST_SUCCEED); + size = sizeof(struct xt_entry_target) + target->size; + target->t = xtables_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + + nft_arp_parse_target(target, data); +} + +static void nft_arp_parse_payload(struct nft_rule_expr_iter *iter, + uint32_t offset, void *data) +{ + struct arpt_entry *fw = data; + struct in_addr addr; + unsigned short int ar_hrd, ar_pro, ar_op, ar_hln; + bool inv; + + switch (offset) { + case offsetof(struct arphdr, ar_hrd): + get_cmp_data(iter, &ar_hrd, sizeof(ar_hrd), &inv); + fw->arp.arhrd = htons(ar_hrd); + fw->arp.arhrd_mask = htons(0xffff); + if (inv) + fw->arp.invflags |= ARPT_INV_ARPHRD; + break; + case offsetof(struct arphdr, ar_pro): + get_cmp_data(iter, &ar_pro, sizeof(ar_pro), &inv); + fw->arp.arpro = htons(ar_pro); + fw->arp.arpro_mask = htons(0xffff); + if (inv) + fw->arp.invflags |= ARPT_INV_ARPPRO; + break; + case offsetof(struct arphdr, ar_op): + get_cmp_data(iter, &ar_op, sizeof(ar_op), &inv); + fw->arp.arpop = htons(ar_op); + fw->arp.arpop_mask = htons(0xffff); + if (inv) + fw->arp.invflags |= ARPT_INV_ARPOP; + break; + case offsetof(struct arphdr, ar_hln): + get_cmp_data(iter, &ar_hln, sizeof(ar_op), &inv); + fw->arp.arhln = ar_hln; + fw->arp.arhln_mask = 0xff; + if (inv) + fw->arp.invflags |= ARPT_INV_ARPOP; + break; + default: + if (!fw->arp.arhln) + break; + + if (offset == sizeof(struct arphdr) + fw->arp.arhln) { + get_cmp_data(iter, &addr, sizeof(addr), &inv); + fw->arp.src.s_addr = addr.s_addr; + fw->arp.smsk.s_addr = 0xffffffff; + if (inv) + fw->arp.invflags |= ARPT_INV_SRCIP; + } else if (offset == sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr)) { + get_cmp_data(iter, &addr, sizeof(addr), &inv); + fw->arp.tgt.s_addr = addr.s_addr; + fw->arp.tmsk.s_addr = 0xffffffff; + if (inv) + fw->arp.invflags |= ARPT_INV_TGTIP; + } + break; + } +} + +static void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw) +{ + struct nft_rule_expr_iter *iter; + struct nft_rule_expr *expr; + int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); + + iter = nft_rule_expr_iter_create(r); + if (iter == NULL) + return; + + expr = nft_rule_expr_iter_next(iter); + while (expr != NULL) { + const char *name = + nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); + + if (strcmp(name, "counter") == 0) + nft_parse_counter(expr, iter, &fw->counters); + else if (strcmp(name, "payload") == 0) + nft_parse_payload(expr, iter, family, fw); + else if (strcmp(name, "meta") == 0) + nft_parse_meta(expr, iter, family, fw); + else if (strcmp(name, "immediate") == 0) + nft_parse_immediate(expr, iter, family, fw); + else if (strcmp(name, "target") == 0) + nft_parse_target(expr, iter, family, fw); + + expr = nft_rule_expr_iter_next(iter); + } + + nft_rule_expr_iter_destroy(iter); +} + +static void +nft_arp_print_firewall(struct nft_rule *r, unsigned int num, + unsigned int format) +{ + struct arpt_entry fw = {}; + const char *targname; + struct xtables_target *target = NULL; + const struct xt_entry_target *t; + char buf[BUFSIZ]; + int i; + char iface[IFNAMSIZ+2]; + int print_iface = 0; + + nft_rule_to_arpt_entry(r, &fw); + + if (format & FMT_LINENUMBERS) + printf("%u ", num); + + if (fw.target_offset) { + t = nft_arp_get_target(&fw); + targname = t->u.user.name; + target = xtables_find_target(targname, XTF_TRY_LOAD); + if (!(format & FMT_NOTARGET)) + printf("-j %s ", targname); + } + + iface[0] = '\0'; + + if (fw.arp.iniface[0] != '\0') { + strcat(iface, fw.arp.iniface); + print_iface = 1; + } + else if (format & FMT_VIA) { + print_iface = 1; + if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + } + if (print_iface) + printf("%s-i %s ", fw.arp.invflags & ARPT_INV_VIA_IN ? "! ": "", iface); + + print_iface = 0; + iface[0] = '\0'; + + if (fw.arp.outiface[0] != '\0') { + strcat(iface, fw.arp.outiface); + print_iface = 1; + } + else if (format & FMT_VIA) { + print_iface = 1; + if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + } + if (print_iface) + printf("%s-o %s ", fw.arp.invflags & ARPT_INV_VIA_OUT ? "! " : "", iface); + + if (fw.arp.smsk.s_addr != 0L) { + printf("%s", fw.arp.invflags & ARPT_INV_SRCIP + ? "! " : ""); + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_dotted(&(fw.arp.src))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw.arp.src))); + strcat(buf, mask_to_dotted(&(fw.arp.smsk))); + printf("-s %s ", buf); + } + + for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++) + if (fw.arp.src_devaddr.mask[i] != 0) + break; + if (i == ARPT_DEV_ADDR_LEN_MAX) + goto after_devsrc; + printf("%s", fw.arp.invflags & ARPT_INV_SRCDEVADDR + ? "! " : ""); + printf("--src-mac "); + print_mac_and_mask((unsigned char *)fw.arp.src_devaddr.addr, + (unsigned char *)fw.arp.src_devaddr.mask, ETH_ALEN); + printf(" "); +after_devsrc: + + if (fw.arp.tmsk.s_addr != 0L) { + printf("%s",fw.arp.invflags & ARPT_INV_TGTIP + ? "! " : ""); + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_dotted(&(fw.arp.tgt))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw.arp.tgt))); + strcat(buf, mask_to_dotted(&(fw.arp.tmsk))); + printf("-d %s ", buf); + } + + for (i = 0; i print) + /* Print the target information. */ + target->print(&fw.arp, t, format & FMT_NUMERIC); + } + + if (!(format & FMT_NOCOUNTS)) { + printf(", pcnt="); + xtables_print_num(fw.counters.pcnt, format); + printf("-- bcnt="); + xtables_print_num(fw.counters.bcnt, format); + } + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); +} + +struct nft_family_ops nft_family_ops_arp = { + .add = nft_arp_add, + .is_same = NULL, + .print_payload = NULL, + .parse_meta = nft_arp_parse_meta, + .parse_payload = nft_arp_parse_payload, + .parse_immediate = nft_arp_parse_immediate, + .print_firewall = nft_arp_print_firewall, + .post_parse = NULL, +}; diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 5681e264..c1cb5a7f 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -30,6 +30,7 @@ extern struct nft_family_ops nft_family_ops_ipv4; extern struct nft_family_ops nft_family_ops_ipv6; +extern struct nft_family_ops nft_family_ops_arp; void add_meta(struct nft_rule *r, uint32_t key) { @@ -281,8 +282,7 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, } } -static void -nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, +void nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, void *data) { size_t tg_len; @@ -381,7 +381,7 @@ void get_cmp_data(struct nft_rule_expr_iter *iter, *inv = false; } -static void +void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, void *data) { @@ -402,7 +402,7 @@ nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, ops->parse_meta(e, key, data); } -static void +void nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, void *data) { @@ -414,7 +414,7 @@ nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, ops->parse_payload(iter, offset, data); } -static void +void nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, struct xt_counters *counters) { @@ -422,7 +422,7 @@ nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, counters->bcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES); } -static void +void nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, void *data) { @@ -649,6 +649,8 @@ struct nft_family_ops *nft_family_ops_lookup(int family) return &nft_family_ops_ipv4; case AF_INET6: return &nft_family_ops_ipv6; + case NFPROTO_ARP: + return &nft_family_ops_arp; default: break; } diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 3f1a9a4a..375c714f 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -84,6 +84,19 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, void print_proto(uint16_t proto, int invert); void get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv); +void nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + int family, void *data); +void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + int family, void *data); +void nft_parse_payload(struct nft_rule_expr *e, + struct nft_rule_expr_iter *iter, + int family, void *data); +void nft_parse_counter(struct nft_rule_expr *e, + struct nft_rule_expr_iter *iter, + struct xt_counters *counters); +void nft_parse_immediate(struct nft_rule_expr *e, + struct nft_rule_expr_iter *iter, + int family, void *data); void nft_rule_to_iptables_command_state(struct nft_rule *r, struct iptables_command_state *cs); void print_firewall_details(const struct iptables_command_state *cs, @@ -149,4 +162,21 @@ struct xtables_args { #define CMD_ZERO_NUM 0x2000U #define CMD_CHECK 0x4000U +/* + * ARP + */ +extern char *opcodes[]; +#define NUMOPCODES 9 + +#include + +static inline struct xt_entry_target *nft_arp_get_target(struct arpt_entry *fw) +{ + struct xt_entry_target **target; + + target = (void *) fw + fw->target_offset; + + return *target; +} + #endif diff --git a/iptables/nft.c b/iptables/nft.c index 49d345d2..28c5473d 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -53,9 +53,9 @@ static void *nft_fn; -static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, - int (*cb)(const struct nlmsghdr *nlh, void *data), - void *data) +int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, + int (*cb)(const struct nlmsghdr *nlh, void *data), + void *data) { int ret; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -210,7 +210,35 @@ struct builtin_table xtables_ipv4[TABLES_MAX] = { }, }; -static int +#include + +struct builtin_table xtables_arp[TABLES_MAX] = { + [FILTER] = { + .name = "filter", + .chains = { + { + .name = "INPUT", + .type = "filter", + .prio = NF_IP_PRI_FILTER, + .hook = NF_ARP_IN, + }, + { + .name = "FORWARD", + .type = "filter", + .prio = NF_IP_PRI_FILTER, + .hook = NF_ARP_FORWARD, + }, + { + .name = "OUTPUT", + .type = "filter", + .prio = NF_IP_PRI_FILTER, + .hook = NF_ARP_OUT, + }, + }, + }, +}; + +int nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, bool dormant) { @@ -242,7 +270,7 @@ nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, return ret; } -static struct nft_chain * +struct nft_chain * nft_chain_builtin_alloc(struct builtin_table *table, struct builtin_chain *chain, int policy) { @@ -262,7 +290,7 @@ nft_chain_builtin_alloc(struct builtin_table *table, return c; } -static void +void nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, struct builtin_chain *chain, int policy) { @@ -288,8 +316,8 @@ nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, } /* find if built-in table already exists */ -static struct builtin_table -*nft_table_builtin_find(struct nft_handle *h, const char *table) +struct builtin_table * +nft_table_builtin_find(struct nft_handle *h, const char *table) { int i; bool found = false; @@ -309,7 +337,7 @@ static struct builtin_table } /* find if built-in chain already exists */ -static struct builtin_chain * +struct builtin_chain * nft_chain_builtin_find(struct builtin_table *t, const char *chain) { int i; @@ -346,7 +374,7 @@ __nft_chain_builtin_init(struct nft_handle *h, } } -static int +int nft_chain_builtin_init(struct nft_handle *h, const char *table, const char *chain, int policy) { @@ -597,7 +625,7 @@ static int __add_target(struct nft_rule_expr *e, struct xt_entry_target *t) return 0; } -static int add_target(struct nft_rule *r, struct xt_entry_target *t) +int add_target(struct nft_rule *r, struct xt_entry_target *t) { struct nft_rule_expr *expr; int ret; @@ -612,7 +640,7 @@ static int add_target(struct nft_rule *r, struct xt_entry_target *t) return ret; } -static int add_jumpto(struct nft_rule *r, const char *name, int verdict) +int add_jumpto(struct nft_rule *r, const char *name, int verdict) { struct nft_rule_expr *expr; @@ -628,7 +656,7 @@ static int add_jumpto(struct nft_rule *r, const char *name, int verdict) return 0; } -static int add_verdict(struct nft_rule *r, int verdict) +int add_verdict(struct nft_rule *r, int verdict) { struct nft_rule_expr *expr; @@ -654,7 +682,7 @@ static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) #endif } -static int add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) +int add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) { struct nft_rule_expr *expr; @@ -1533,12 +1561,12 @@ __nft_rule_del(struct nft_handle *h, struct nft_rule *r) perror("mnl_talk:nft_rule_del"); } -static struct nft_rule_list *nft_rule_list_create(struct nft_handle *h) +struct nft_rule_list *nft_rule_list_create(struct nft_handle *h) { return nft_rule_list_get(h); } -static void nft_rule_list_destroy(struct nft_rule_list *list) +void nft_rule_list_destroy(struct nft_rule_list *list) { nft_rule_list_free(list); } @@ -2443,3 +2471,246 @@ err: return ret == 0 ? 1 : 0; } +/* + * XXX These are almost copy and paste of the original functions, but adapted + * to ARP. These functions should go away with some refactoring of the + * existing infrastructure. + */ +static struct nft_rule * +nft_arp_rule_new(struct nft_handle *h, const char *chain, const char *table, + struct arpt_entry *fw) +{ + struct xt_entry_target *t; + char *targname; + struct nft_rule *r; + int ret = 0; + + t = nft_arp_get_target(fw); + targname = t->u.user.name; + + r = nft_rule_alloc(); + if (r == NULL) + return NULL; + + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, h->family); + nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); + nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); + + h->ops->add(r, fw); + + /* Counters need to me added before the target, otherwise they are + * increased for each rule because of the way nf_tables works. + */ + if (add_counters(r, fw->counters.pcnt, fw->counters.bcnt) < 0) + goto err; + + /* Standard target? */ + if (strcmp(targname, XTC_LABEL_ACCEPT) == 0) + ret = add_verdict(r, NF_ACCEPT); + else if (strcmp(targname, XTC_LABEL_DROP) == 0) + ret = add_verdict(r, NF_DROP); + else if (strcmp(targname, XTC_LABEL_RETURN) == 0) + ret = add_verdict(r, NFT_RETURN); + else if (xtables_find_target(targname, XTF_TRY_LOAD) != NULL) + ret = add_target(r, t); + else + ret = add_jumpto(r, targname, NFT_JUMP); + + if (ret < 0) + goto err; + + return r; +err: + nft_rule_free(r); + return NULL; + +} + +static void +nft_arp_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) { +#ifdef NLDEBUG + char tmp[1024]; + + nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0); + printf("DEBUG: rule: %s", tmp); + mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg)); +#endif +} + +static int +nft_arp_rule_add(struct nft_handle *h, const char *chain, + const char *table, struct arpt_entry *fw, + uint64_t handle, bool verbose) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_rule *r; + int ret = 1; + + r = nft_arp_rule_new(h, chain, table, fw); + if (r == NULL) { + ret = 0; + goto err; + } + if (handle > 0) + nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle); + + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } + + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, + NLM_F_ACK|NLM_F_CREATE, h->seq); + nft_rule_nlmsg_build_payload(nlh, r); + nft_arp_rule_print_debug(r, nlh); + nft_rule_free(r); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) + perror("mnl_talk:nft_arp_rule_add"); + +err: + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +int nft_arp_rule_append(struct nft_handle *h, const char *chain, + const char *table, struct arpt_entry *fw, bool verbose) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_rule *r; + uint16_t flags = NLM_F_ACK|NLM_F_CREATE; + int ret = 1; + + /* If built-in chains don't exist for this table, create them */ + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) + nft_chain_builtin_init(h, table, chain, NF_ACCEPT); + + nft_fn = nft_arp_rule_append; + + r = nft_arp_rule_new(h, chain, table, fw); + if (r == NULL) { + ret = 0; + goto err; + } + + /*if (handle > 0) { + nft_rule_attr_set(r, NFT_RULE_ATTR_HANDLE, &handle); + flags |= NLM_F_REPLACE; + } else + flags |= NLM_F_APPEND;*/ + + flags |= NLM_F_APPEND; + + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } + + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, + flags, h->seq); + nft_rule_nlmsg_build_payload(nlh, r); + nft_arp_rule_print_debug(r, nlh); + nft_rule_free(r); + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) + perror("mnl_talk:nft_arp_rule_append"); + +err: + /* the core expects 1 for success and 0 for error */ + return ret == 0 ? 1 : 0; +} + +static struct nft_rule * +nft_arp_rule_find(struct nft_rule_list *list, const char *chain, + const char *table, struct arpt_entry *fw, int rulenum) +{ + struct nft_rule *r; + struct nft_rule_list_iter *iter; + int rule_ctr = 0; + bool found = false; + + iter = nft_rule_list_iter_create(list); + if (iter == NULL) + return 0; + + r = nft_rule_list_iter_next(iter); + while (r != NULL) { + const char *rule_table = + nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE); + const char *rule_chain = + nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); + const struct nft_family_ops *ops = nft_family_ops_lookup( + nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)); + struct arpt_entry this = {}; + + if (strcmp(table, rule_table) != 0 || + strcmp(chain, rule_chain) != 0) { + DEBUGP("different chain / table\n"); + goto next; + } + + if (rulenum >= 0) { + /* Delete by rule number case */ + if (rule_ctr != rulenum) + goto next; + found = true; + break; + } else { + /* Delete by matching rule case */ + nft_rule_to_arpt_entry(r, &this); + + DEBUGP("comparing with... "); + + found = true; + break; + } +next: + rule_ctr++; + r = nft_rule_list_iter_next(iter); + } + + nft_rule_list_iter_destroy(iter); + + return found ? r : NULL; +} + +int nft_arp_rule_insert(struct nft_handle *h, const char *chain, + const char *table, struct arpt_entry *fw, + int rulenum, bool verbose) +{ + struct nft_rule_list *list; + struct nft_rule *r; + uint64_t handle = 0; + + /* If built-in chains don't exist for this table, create them */ + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) + nft_chain_builtin_init(h, table, chain, NF_ACCEPT); + + nft_fn = nft_arp_rule_insert; + + if (rulenum > 0) { + list = nft_rule_list_create(h); + if (list == NULL) + goto err; + + r = nft_arp_rule_find(list, chain, table, fw, rulenum); + if (r == NULL) { + errno = ENOENT; + goto err; + } + + handle = nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE); + DEBUGP("adding after rule handle %"PRIu64"\n", handle); + + nft_rule_list_destroy(list); + } + + return nft_arp_rule_add(h, chain, table, fw, handle, verbose); +err: + nft_rule_list_destroy(list); + return 0; +} diff --git a/iptables/nft.h b/iptables/nft.h index fe1b9c81..25275cee 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -34,7 +34,11 @@ struct nft_handle { }; extern struct builtin_table xtables_ipv4[TABLES_MAX]; +extern struct builtin_table xtables_arp[TABLES_MAX]; +int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, + int (*cb)(const struct nlmsghdr *nlh, void *data), + void *data); int nft_init(struct nft_handle *h, struct builtin_table *t); void nft_fini(struct nft_handle *h); @@ -44,6 +48,8 @@ void nft_fini(struct nft_handle *h); struct nft_table; struct nft_chain_list; +int nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, bool dormant); +struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *table); int nft_table_add(struct nft_handle *h, const struct nft_table *t); int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, bool counters), bool counters); bool nft_table_find(struct nft_handle *h, const char *tablename); @@ -56,6 +62,10 @@ int nft_table_purge_chains(struct nft_handle *h, const char *table, struct nft_c */ struct nft_chain; +struct nft_chain *nft_chain_builtin_alloc(struct builtin_table *table, struct builtin_chain *chain, int policy); +void nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, struct builtin_chain *chain, int policy); +struct builtin_chain *nft_chain_builtin_find(struct builtin_table *t, const char *chain); +int nft_chain_builtin_init(struct nft_handle *h, const char *table, const char *chain, int policy); int nft_chain_add(struct nft_handle *h, const struct nft_chain *c); int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters); struct nft_chain_list *nft_chain_dump(struct nft_handle *h); @@ -82,6 +92,16 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *tabl int nft_rule_save(struct nft_handle *h, const char *table, bool counters); int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table); int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *table, int rulenum); +struct nft_rule_list *nft_rule_list_create(struct nft_handle *h); +void nft_rule_list_destroy(struct nft_rule_list *list); + +/* + * Operations used in userspace tools + */ +int add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes); +int add_verdict(struct nft_rule *r, int verdict); +int add_target(struct nft_rule *r, struct xt_entry_target *t); +int add_jumpto(struct nft_rule *r, const char *name, int verdict); enum nft_rule_print { NFT_RULE_APPEND, @@ -110,6 +130,8 @@ const char *nft_strerror(int err); /* For xtables.c */ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table); +/* For xtables-arptables.c */ +int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table); /* * Parse config for tables and chain helper functions @@ -127,4 +149,17 @@ enum { int nft_xtables_config_load(struct nft_handle *h, const char *filename, uint32_t flags); +/* + * ARP + */ + +struct arpt_entry; + +int nft_arp_rule_append(struct nft_handle *h, const char *chain, + const char *table, struct arpt_entry *fw, + bool verbose); +int nft_arp_rule_insert(struct nft_handle *h, const char *chain, + const char *table, struct arpt_entry *fw, + int rulenum, bool verbose); + #endif diff --git a/iptables/xtables-arp-standalone.c b/iptables/xtables-arp-standalone.c new file mode 100644 index 00000000..186c9a1e --- /dev/null +++ b/iptables/xtables-arp-standalone.c @@ -0,0 +1,86 @@ +/* + * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au + * + * Based on the ipchains code by Paul Russell and Michael Neuling + * + * (C) 2000-2002 by the netfilter coreteam : + * Paul 'Rusty' Russell + * Marc Boucher + * James Morris + * Harald Welte + * Jozsef Kadlecsik + * + * arptables -- IP firewall administration for kernels with + * firewall table (aimed for the 2.3 kernels) + * + * See the accompanying manual page arptables(8) for information + * about proper usage of this program. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include "nft.h" +#include + +#include "xtables-multi.h" + +extern struct xtables_globals xtables_globals; +extern const char *program_version, *program_name; + +static const struct xtables_afinfo afinfo_arp = { + .kmod = "arp_tables", + .proc_exists = "/proc/net/arp_tables_names", + .libprefix = "libarp_", + .family = NFPROTO_ARP, + .ipproto = IPPROTO_IP, + .so_rev_match = -1, + .so_rev_target = -1, +}; + +int xtables_arp_main(int argc, char *argv[]) +{ + int ret; + char *table = "filter"; + struct nft_handle h = { + .family = NFPROTO_ARP, + }; + + xtables_globals.program_name = "xtables-arp"; + /* This code below could be replaced by xtables_init_all, which + * doesn't support NFPROTO_ARP yet. + */ + xtables_init(); + afinfo = &afinfo_arp; + ret = xtables_set_params(&xtables_globals); + if (ret < 0) { + fprintf(stderr, "%s/%s Failed to initialize xtables\n", + xtables_globals.program_name, + xtables_globals.program_version); + exit(1); + } + +#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) + init_extensions(); +#endif + + ret = do_commandarp(&h, argc, argv, &table); + + exit(!ret); +} diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c new file mode 100644 index 00000000..68687c0e --- /dev/null +++ b/iptables/xtables-arp.c @@ -0,0 +1,1457 @@ +/* Code to take an arptables-style command line and do it. */ + +/* + * arptables: + * Author: Bart De Schuymer , but + * almost all code is from the iptables userspace program, which has main + * authors: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + Currently, only support for specifying hardware addresses for Ethernet + is available. + This tool is not luser-proof: you can specify an Ethernet source address + and set hardware length to something different than 6, f.e. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xshared.h" + +#include "nft.h" +#include + +typedef char arpt_chainlabel[32]; + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* XXX: command defined by nft-shared.h do not overlap with these two */ +#undef CMD_CHECK +#undef CMD_RENAME_CHAIN + +#define CMD_NONE 0x0000U +#define CMD_INSERT 0x0001U +#define CMD_DELETE 0x0002U +#define CMD_DELETE_NUM 0x0004U +#define CMD_REPLACE 0x0008U +#define CMD_APPEND 0x0010U +#define CMD_LIST 0x0020U +#define CMD_FLUSH 0x0040U +#define CMD_ZERO 0x0080U +#define CMD_NEW_CHAIN 0x0100U +#define CMD_DELETE_CHAIN 0x0200U +#define CMD_SET_POLICY 0x0400U +#define CMD_CHECK 0x0800U +#define CMD_RENAME_CHAIN 0x1000U +#define NUMBER_OF_CMD 13 +static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', + 'N', 'X', 'P', 'E' }; + +#define OPTION_OFFSET 256 + +#define OPT_NONE 0x00000U +#define OPT_NUMERIC 0x00001U +#define OPT_S_IP 0x00002U +#define OPT_D_IP 0x00004U +#define OPT_S_MAC 0x00008U +#define OPT_D_MAC 0x00010U +#define OPT_H_LENGTH 0x00020U +#define OPT_P_LENGTH 0x00040U +#define OPT_OPCODE 0x00080U +#define OPT_H_TYPE 0x00100U +#define OPT_P_TYPE 0x00200U +#define OPT_JUMP 0x00400U +#define OPT_VERBOSE 0x00800U +#define OPT_VIANAMEIN 0x01000U +#define OPT_VIANAMEOUT 0x02000U +#define OPT_LINENUMBERS 0x04000U +#define OPT_COUNTERS 0x08000U +#define NUMBER_OF_OPT 16 +static const char optflags[NUMBER_OF_OPT] += { 'n', 's', 'd', 2, 3, 7, 8, 4, 5, 6, 'j', 'v', 'i', 'o', '0', 'c'}; + +static struct option original_opts[] = { + { "append", 1, 0, 'A' }, + { "delete", 1, 0, 'D' }, + { "insert", 1, 0, 'I' }, + { "replace", 1, 0, 'R' }, + { "list", 2, 0, 'L' }, + { "flush", 2, 0, 'F' }, + { "zero", 2, 0, 'Z' }, + { "new-chain", 1, 0, 'N' }, + { "delete-chain", 2, 0, 'X' }, + { "rename-chain", 1, 0, 'E' }, + { "policy", 1, 0, 'P' }, + { "source-ip", 1, 0, 's' }, + { "destination-ip", 1, 0, 'd' }, + { "src-ip", 1, 0, 's' }, + { "dst-ip", 1, 0, 'd' }, + { "source-mac", 1, 0, 2}, + { "destination-mac", 1, 0, 3}, + { "src-mac", 1, 0, 2}, + { "dst-mac", 1, 0, 3}, + { "h-length", 1, 0, 'l' }, + { "p-length", 1, 0, 8 }, + { "opcode", 1, 0, 4 }, + { "h-type", 1, 0, 5 }, + { "proto-type", 1, 0, 6 }, + { "in-interface", 1, 0, 'i' }, + { "jump", 1, 0, 'j' }, + { "table", 1, 0, 't' }, + { "match", 1, 0, 'm' }, + { "numeric", 0, 0, 'n' }, + { "out-interface", 1, 0, 'o' }, + { "verbose", 0, 0, 'v' }, + { "exact", 0, 0, 'x' }, + { "version", 0, 0, 'V' }, + { "help", 2, 0, 'h' }, + { "line-numbers", 0, 0, '0' }, + { "modprobe", 1, 0, 'M' }, + { 0 } +}; + +int RUNTIME_NF_ARP_NUMHOOKS = 3; + +static struct option *opts = original_opts; +static unsigned int global_option_offset = 0; + +extern void xtables_exit_error(enum xtables_exittype status, const char *msg, ...); +extern struct xtables_globals xtables_globals; + +/* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal (applies to + * CMD_LIST and CMD_ZERO only). + * Key: + * + compulsory + * x illegal + * optional + */ + +static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = +/* Well, it's better than "Re: Linux vs FreeBSD" */ +{ + /* -n -s -d -p -j -v -x -i -o -f --line */ +/*INSERT*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*DELETE*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*DELETE_NUM*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*REPLACE*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*APPEND*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*LIST*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*FLUSH*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*ZERO*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*NEW_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*DEL_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*SET_POLICY*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*CHECK*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, +/*RENAME*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '} +}; + +static int inverse_for_options[NUMBER_OF_OPT] = +{ +/* -n */ 0, +/* -s */ ARPT_INV_SRCIP, +/* -d */ ARPT_INV_TGTIP, +/* 2 */ ARPT_INV_SRCDEVADDR, +/* 3 */ ARPT_INV_TGTDEVADDR, +/* -l */ ARPT_INV_ARPHLN, +/* 8 */ 0, +/* 4 */ ARPT_INV_ARPOP, +/* 5 */ ARPT_INV_ARPHRD, +/* 6 */ ARPT_INV_ARPPRO, +/* -j */ 0, +/* -v */ 0, +/* -i */ ARPT_INV_VIA_IN, +/* -o */ ARPT_INV_VIA_OUT, +/*--line*/ 0, +/* -c */ 0, +}; + +const char *program_version = XTABLES_VERSION; +const char *program_name = "xtables-arp"; + +/* A few hardcoded protocols for 'all' and in case the user has no + /etc/protocols */ +struct pprot { + char *name; + u_int8_t num; +}; + +/* Primitive headers... */ +/* defined in netinet/in.h */ +#if 0 +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif +#endif + +/***********************************************/ +/* ARPTABLES SPECIFIC NEW FUNCTIONS ADDED HERE */ +/***********************************************/ + +unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0}; +unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0}; +unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; +unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; +unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; +unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; + +/* + * put the mac address into 6 (ETH_ALEN) bytes + */ +static int getmac_and_mask(char *from, char *to, char *mask) +{ + char *p; + int i; + struct ether_addr *addr; + + if (strcasecmp(from, "Unicast") == 0) { + memcpy(to, mac_type_unicast, ETH_ALEN); + memcpy(mask, msk_type_unicast, ETH_ALEN); + return 0; + } + if (strcasecmp(from, "Multicast") == 0) { + memcpy(to, mac_type_multicast, ETH_ALEN); + memcpy(mask, msk_type_multicast, ETH_ALEN); + return 0; + } + if (strcasecmp(from, "Broadcast") == 0) { + memcpy(to, mac_type_broadcast, ETH_ALEN); + memcpy(mask, msk_type_broadcast, ETH_ALEN); + return 0; + } + if ( (p = strrchr(from, '/')) != NULL) { + *p = '\0'; + if (!(addr = ether_aton(p + 1))) + return -1; + memcpy(mask, addr, ETH_ALEN); + } else + memset(mask, 0xff, ETH_ALEN); + if (!(addr = ether_aton(from))) + return -1; + memcpy(to, addr, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + to[i] &= mask[i]; + return 0; +} + +static int getlength_and_mask(char *from, uint8_t *to, uint8_t *mask) +{ + char *p, *buffer; + int i; + + if ( (p = strrchr(from, '/')) != NULL) { + *p = '\0'; + i = strtol(p+1, &buffer, 10); + if (*buffer != '\0' || i < 0 || i > 255) + return -1; + *mask = (uint8_t)i; + } else + *mask = 255; + i = strtol(from, &buffer, 10); + if (*buffer != '\0' || i < 0 || i > 255) + return -1; + *to = (uint8_t)i; + return 0; +} + +static int get16_and_mask(char *from, uint16_t *to, uint16_t *mask, int base) +{ + char *p, *buffer; + int i; + + if ( (p = strrchr(from, '/')) != NULL) { + *p = '\0'; + i = strtol(p+1, &buffer, base); + if (*buffer != '\0' || i < 0 || i > 65535) + return -1; + *mask = htons((uint16_t)i); + } else + *mask = 65535; + i = strtol(from, &buffer, base); + if (*buffer != '\0' || i < 0 || i > 65535) + return -1; + *to = htons((uint16_t)i); + return 0; +} + +static int +string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtol(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && number <= max) { + *ret = number; + return 0; + } + } + return -1; +} + +/*********************************************/ +/* ARPTABLES SPECIFIC NEW FUNCTIONS END HERE */ +/*********************************************/ + +static struct in_addr * +dotted_to_addr(const char *dotted) +{ + static struct in_addr addr; + unsigned char *addrp; + char *p, *q; + unsigned int onebyte; + int i; + char buf[20]; + + /* copy dotted string, because we need to modify it */ + strncpy(buf, dotted, sizeof(buf) - 1); + addrp = (unsigned char *) &(addr.s_addr); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return (struct in_addr *) NULL; + + *q = '\0'; + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[i] = (unsigned char) onebyte; + p = q + 1; + } + + /* we've checked 3 bytes, now we check the last one */ + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[3] = (unsigned char) onebyte; + + return &addr; +} + +static struct in_addr * +network_to_addr(const char *name) +{ + struct netent *net; + static struct in_addr addr; + + if ((net = getnetbyname(name)) != NULL) { + if (net->n_addrtype != AF_INET) + return (struct in_addr *) NULL; + addr.s_addr = htonl((unsigned long) net->n_net); + return &addr; + } + + return (struct in_addr *) NULL; +} + +static void +inaddrcpy(struct in_addr *dst, struct in_addr *src) +{ + /* memcpy(dst, src, sizeof(struct in_addr)); */ + dst->s_addr = src->s_addr; +} + +static void +exit_tryhelp(int status) +{ + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + program_name, program_name ); + exit(status); +} + +static void +exit_printhelp(void) +{ + struct xtables_target *t = NULL; + int i; + + printf("%s v%s\n\n" +"Usage: %s -[AD] chain rule-specification [options]\n" +" %s -[RI] chain rulenum rule-specification [options]\n" +" %s -D chain rulenum [options]\n" +" %s -[LFZ] [chain] [options]\n" +" %s -[NX] chain\n" +" %s -E old-chain-name new-chain-name\n" +" %s -P chain target [options]\n" +" %s -h (print this help information)\n\n", + program_name, program_version, program_name, program_name, + program_name, program_name, program_name, program_name, + program_name, program_name); + + printf( +"Commands:\n" +"Either long or short options are allowed.\n" +" --append -A chain Append to chain\n" +" --delete -D chain Delete matching rule from chain\n" +" --delete -D chain rulenum\n" +" Delete rule rulenum (1 = first) from chain\n" +" --insert -I chain [rulenum]\n" +" Insert in chain as rulenum (default 1=first)\n" +" --replace -R chain rulenum\n" +" Replace rule rulenum (1 = first) in chain\n" +" --list -L [chain] List the rules in a chain or all chains\n" +" --flush -F [chain] Delete all rules in chain or all chains\n" +" --zero -Z [chain] Zero counters in chain or all chains\n" +" --new -N chain Create a new user-defined chain\n" +" --delete-chain\n" +" -X [chain] Delete a user-defined chain\n" +" --policy -P chain target\n" +" Change policy on chain to target\n" +" --rename-chain\n" +" -E old-chain new-chain\n" +" Change chain name, (moving any references)\n" + +"Options:\n" +" --source-ip -s [!] address[/mask]\n" +" source specification\n" +" --destination-ip -d [!] address[/mask]\n" +" destination specification\n" +" --source-mac [!] address[/mask]\n" +" --destination-mac [!] address[/mask]\n" +" --h-length -l length[/mask] hardware length (nr of bytes)\n" +" --opcode code[/mask] operation code (2 bytes)\n" +" --h-type type[/mask] hardware type (2 bytes, hexadecimal)\n" +" --proto-type type[/mask] protocol type (2 bytes)\n" +" --in-interface -i [!] input name[+]\n" +" network interface name ([+] for wildcard)\n" +" --out-interface -o [!] output name[+]\n" +" network interface name ([+] for wildcard)\n" +" --jump -j target\n" +" target for rule (may load target extension)\n" +" --match -m match\n" +" extended match (may load extension)\n" +" --numeric -n numeric output of addresses and ports\n" +" --table -t table table to manipulate (default: `filter')\n" +" --verbose -v verbose mode\n" +" --line-numbers print line numbers when listing\n" +" --exact -x expand numbers (display exact values)\n" +" --modprobe= try to insert modules using this command\n" +" --set-counters PKTS BYTES set the counter during insert/append\n" +"[!] --version -V print package version.\n"); + printf(" opcode strings: \n"); + for (i = 0; i < NUMOPCODES; i++) + printf(" %d = %s\n", i + 1, opcodes[i]); + printf( +" hardware type string: 1 = Ethernet\n" +" protocol type string: 0x800 = IPv4\n"); + + /* Print out any special helps. A user might like to be able + to add a --help to the commandline, and see expected + results. So we call help for all matches & targets */ + for (t = xtables_targets; t; t = t->next) { + if (strcmp(t->name, "CLASSIFY") && strcmp(t->name, "mangle")) + continue; + printf("\n"); + t->help(); + } + exit(0); +} + +static void +generic_opt_check(int command, int options) +{ + int i, j, legal = 0; + + /* Check that commands are valid with options. Complicated by the + * fact that if an option is legal with *any* command given, it is + * legal overall (ie. -z and -l). + */ + for (i = 0; i < NUMBER_OF_OPT; i++) { + legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ + + for (j = 0; j < NUMBER_OF_CMD; j++) { + if (!(command & (1< 1; option >>= 1, ptr++); + + return *ptr; +} + +static char +cmd2char(int option) +{ + const char *ptr; + for (ptr = cmdflags; option > 1; option >>= 1, ptr++); + + return *ptr; +} + +static void +add_command(unsigned int *cmd, const int newcmd, const unsigned int othercmds, int invert) +{ + if (invert) + xtables_error(PARAMETER_PROBLEM, "unexpected ! flag"); + if (*cmd & (~othercmds)) + xtables_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", + cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); + *cmd |= newcmd; +} + +static int +check_inverse(const char option[], int *invert, int *optind, int argc) +{ + if (option && strcmp(option, "!") == 0) { + if (*invert) + xtables_error(PARAMETER_PROBLEM, + "Multiple `!' flags not allowed"); + *invert = TRUE; + if (optind) { + *optind = *optind+1; + if (argc && *optind > argc) + xtables_error(PARAMETER_PROBLEM, + "no argument following `!'"); + } + + return TRUE; + } + return FALSE; +} + +static struct in_addr * +host_to_addr(const char *name, unsigned int *naddr) +{ + struct hostent *host; + struct in_addr *addr; + unsigned int i; + + *naddr = 0; + if ((host = gethostbyname(name)) != NULL) { + if (host->h_addrtype != AF_INET || + host->h_length != sizeof(struct in_addr)) + return (struct in_addr *) NULL; + + while (host->h_addr_list[*naddr] != (char *) NULL) + (*naddr)++; + addr = xtables_calloc(*naddr, sizeof(struct in_addr)); + for (i = 0; i < *naddr; i++) + inaddrcpy(&(addr[i]), + (struct in_addr *) host->h_addr_list[i]); + return addr; + } + + return (struct in_addr *) NULL; +} + +/* + * All functions starting with "parse" should succeed, otherwise + * the program fails. + * Most routines return pointers to static data that may change + * between calls to the same or other routines with a few exceptions: + * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask" + * return global static data. +*/ + +static struct in_addr * +parse_hostnetwork(const char *name, unsigned int *naddrs) +{ + struct in_addr *addrp, *addrptmp; + + if ((addrptmp = dotted_to_addr(name)) != NULL || + (addrptmp = network_to_addr(name)) != NULL) { + addrp = xtables_malloc(sizeof(struct in_addr)); + inaddrcpy(addrp, addrptmp); + *naddrs = 1; + return addrp; + } + if ((addrp = host_to_addr(name, naddrs)) != NULL) + return addrp; + + xtables_error(PARAMETER_PROBLEM, "host/network `%s' not found", name); +} + +static struct in_addr * +parse_mask(char *mask) +{ + static struct in_addr maskaddr; + struct in_addr *addrp; + unsigned int bits; + + if (mask == NULL) { + /* no mask at all defaults to 32 bits */ + maskaddr.s_addr = 0xFFFFFFFF; + return &maskaddr; + } + if ((addrp = dotted_to_addr(mask)) != NULL) + /* dotted_to_addr already returns a network byte order addr */ + return addrp; + if (string_to_number(mask, 0, 32, &bits) == -1) + xtables_error(PARAMETER_PROBLEM, + "invalid mask `%s' specified", mask); + if (bits != 0) { + maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits)); + return &maskaddr; + } + + maskaddr.s_addr = 0L; + return &maskaddr; +} + +static void +parse_hostnetworkmask(const char *name, struct in_addr **addrpp, + struct in_addr *maskp, unsigned int *naddrs) +{ + struct in_addr *addrp; + char buf[256]; + char *p; + int i, j, k, n; + + strncpy(buf, name, sizeof(buf) - 1); + if ((p = strrchr(buf, '/')) != NULL) { + *p = '\0'; + addrp = parse_mask(p + 1); + } else + addrp = parse_mask(NULL); + inaddrcpy(maskp, addrp); + + /* if a null mask is given, the name is ignored, like in "any/0" */ + if (maskp->s_addr == 0L) + strcpy(buf, "0.0.0.0"); + + addrp = *addrpp = parse_hostnetwork(buf, naddrs); + n = *naddrs; + for (i = 0, j = 0; i < n; i++) { + addrp[j++].s_addr &= maskp->s_addr; + for (k = 0; k < j - 1; k++) { + if (addrp[k].s_addr == addrp[j - 1].s_addr) { + (*naddrs)--; + j--; + break; + } + } + } +} + +static void +parse_interface(const char *arg, char *vianame, unsigned char *mask) +{ + int vialen = strlen(arg); + unsigned int i; + + memset(mask, 0, IFNAMSIZ); + memset(vianame, 0, IFNAMSIZ); + + if (vialen + 1 > IFNAMSIZ) + xtables_error(PARAMETER_PROBLEM, + "interface name `%s' must be shorter than IFNAMSIZ" + " (%i)", arg, IFNAMSIZ-1); + + strcpy(vianame, arg); + if (vialen == 0) + memset(mask, 0, IFNAMSIZ); + else if (vianame[vialen - 1] == '+') { + memset(mask, 0xFF, vialen - 1); + memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); + /* Don't remove `+' here! -HW */ + } else { + /* Include nul-terminator in match */ + memset(mask, 0xFF, vialen + 1); + memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); + for (i = 0; vianame[i]; i++) { + if (!isalnum(vianame[i]) + && vianame[i] != '_' + && vianame[i] != '.') { + printf("Warning: wierd character in interface" + " `%s' (No aliases, :, ! or *).\n", + vianame); + break; + } + } + } +} + +/* Can't be zero. */ +static int +parse_rulenumber(const char *rule) +{ + unsigned int rulenum; + + if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1) + xtables_error(PARAMETER_PROBLEM, + "Invalid rule number `%s'", rule); + + return rulenum; +} + +static const char * +parse_target(const char *targetname) +{ + const char *ptr; + + if (strlen(targetname) < 1) + xtables_error(PARAMETER_PROBLEM, + "Invalid target name (too short)"); + + if (strlen(targetname)+1 > sizeof(arpt_chainlabel)) + xtables_error(PARAMETER_PROBLEM, + "Invalid target name `%s' (%zu chars max)", + targetname, sizeof(arpt_chainlabel)-1); + + for (ptr = targetname; *ptr; ptr++) + if (isspace(*ptr)) + xtables_error(PARAMETER_PROBLEM, + "Invalid target name `%s'", targetname); + return targetname; +} + +static void +set_option(unsigned int *options, unsigned int option, u_int16_t *invflg, + int invert) +{ + if (*options & option) + xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", + opt2char(option)); + *options |= option; + + if (invert) { + unsigned int i; + for (i = 0; 1 << i != option; i++); + + if (!inverse_for_options[i]) + xtables_error(PARAMETER_PROBLEM, + "cannot have ! before -%c", + opt2char(option)); + *invflg |= inverse_for_options[i]; + } +} + +static int +list_entries(struct nft_handle *h, const char *chain, const char *table, + int rulenum, int verbose, int numeric, int expanded, + int linenumbers) +{ + unsigned int format; + + format = FMT_OPTIONS; + if (!verbose) + format |= FMT_NOCOUNTS; + else + format |= FMT_VIA; + + if (numeric) + format |= FMT_NUMERIC; + + if (!expanded) + format |= FMT_KILOMEGAGIGA; + + if (linenumbers) + format |= FMT_LINENUMBERS; + + return nft_rule_list(h, chain, table, rulenum, format); +} + +static struct arpt_entry * +generate_entry(const struct arpt_entry *fw, + struct arpt_entry_target *target) +{ + struct arpt_entry_target **t; + struct arpt_entry *e; + unsigned int size; + + + size = sizeof(struct arpt_entry); + + e = xtables_malloc(size + target->u.target_size); + *e = *fw; + e->target_offset = size; + e->next_offset = size + target->u.target_size; + + t = (void *) e + e->target_offset; + *t = target; + + return e; +} + +static struct xtables_target *command_jump(struct arpt_entry *fw, + const char *jumpto) +{ + struct xtables_target *target; + size_t size; + + /* XTF_TRY_LOAD (may be chain name) */ + target = xtables_find_target(jumpto, XTF_TRY_LOAD); + + if (!target) + return NULL; + + size = XT_ALIGN(sizeof(struct xt_entry_target)) + + target->size; + + target->t = xtables_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->t->u.user.revision = target->revision; + + xs_init_target(target); + + if (target->x6_options != NULL) + opts = xtables_options_xfrm(xtables_globals.orig_opts, + opts, target->x6_options, + &target->option_offset); + else + opts = xtables_merge_options(xtables_globals.orig_opts, + opts, target->extra_opts, + &target->option_offset); + + return target; +} + +static int +append_entry(struct nft_handle *h, + const char *chain, + const char *table, + struct arpt_entry *fw, + int rulenum, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + bool verbose, bool append) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->arp.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->arp.tgt.s_addr = daddrs[j].s_addr; + if (append) { + ret = nft_arp_rule_append(h, chain, table, + fw, verbose); + } else { + ret = nft_arp_rule_insert(h, chain, table, + fw, rulenum, + verbose); + } + } + } + + return ret; +} + +int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) +{ + struct arpt_entry fw, *e = NULL; + int invert = 0; + unsigned int nsaddrs = 0, ndaddrs = 0; + struct in_addr *saddrs = NULL, *daddrs = NULL; + + int c, verbose = 0; + const char *chain = NULL; + const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; + const char *policy = NULL, *newname = NULL; + unsigned int rulenum = 0, options = 0, command = 0; + const char *pcnt = NULL, *bcnt = NULL; + int ret = 1; + struct xtables_target *target = NULL; + struct xtables_target *t; + + const char *jumpto = ""; + + memset(&fw, 0, sizeof(fw)); + opts = original_opts; + global_option_offset = 0; + + xtables_globals.orig_opts = original_opts; + + /* re-set optind to 0 in case do_command gets called + * a second time */ + optind = 0; + + for (t = xtables_targets; t; t = t->next) { + t->tflags = 0; + t->used = 0; + } + + /* Suppress error messages: we may add new options if we + demand-load a protocol. */ + opterr = 0; + + while ((c = getopt_long(argc, argv, + "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:l:i:vnt:m:c:", + opts, NULL)) != -1) { + switch (c) { + /* + * Command selection + */ + case 'A': + add_command(&command, CMD_APPEND, CMD_NONE, + invert); + chain = optarg; + break; + + case 'D': + add_command(&command, CMD_DELETE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') { + rulenum = parse_rulenumber(argv[optind++]); + command = CMD_DELETE_NUM; + } + break; + + case 'R': + add_command(&command, CMD_REPLACE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires a rule number", + cmd2char(CMD_REPLACE)); + break; + + case 'I': + add_command(&command, CMD_INSERT, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else rulenum = 1; + break; + + case 'L': + add_command(&command, CMD_LIST, CMD_ZERO, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'F': + add_command(&command, CMD_FLUSH, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'Z': + add_command(&command, CMD_ZERO, CMD_LIST, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'N': + if (optarg && *optarg == '-') + xtables_error(PARAMETER_PROBLEM, + "chain name not allowed to start " + "with `-'\n"); + if (xtables_find_target(optarg, XTF_TRY_LOAD)) + xtables_error(PARAMETER_PROBLEM, + "chain name may not clash " + "with target name\n"); + add_command(&command, CMD_NEW_CHAIN, CMD_NONE, + invert); + chain = optarg; + break; + + case 'X': + add_command(&command, CMD_DELETE_CHAIN, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'E': + add_command(&command, CMD_RENAME_CHAIN, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + newname = argv[optind++]; + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires old-chain-name and " + "new-chain-name", + cmd2char(CMD_RENAME_CHAIN)); + break; + + case 'P': + add_command(&command, CMD_SET_POLICY, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + policy = argv[optind++]; + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires a chain and a policy", + cmd2char(CMD_SET_POLICY)); + break; + + case 'h': + if (!optarg) + optarg = argv[optind]; + + exit_printhelp(); + break; + case 's': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_S_IP, &fw.arp.invflags, + invert); + shostnetworkmask = argv[optind-1]; + break; + + case 'd': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_D_IP, &fw.arp.invflags, + invert); + dhostnetworkmask = argv[optind-1]; + break; + + case 2:/* src-mac */ + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_S_MAC, &fw.arp.invflags, + invert); + if (getmac_and_mask(argv[optind - 1], + fw.arp.src_devaddr.addr, fw.arp.src_devaddr.mask)) + xtables_error(PARAMETER_PROBLEM, "Problem with specified " + "source mac"); + break; + + case 3:/* dst-mac */ + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_D_MAC, &fw.arp.invflags, + invert); + + if (getmac_and_mask(argv[optind - 1], + fw.arp.tgt_devaddr.addr, fw.arp.tgt_devaddr.mask)) + xtables_error(PARAMETER_PROBLEM, "Problem with specified " + "destination mac"); + break; + + case 'l':/* hardware length */ + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_H_LENGTH, &fw.arp.invflags, + invert); + getlength_and_mask(argv[optind - 1], &fw.arp.arhln, + &fw.arp.arhln_mask); + break; + + case 8:/* protocol length */ + xtables_error(PARAMETER_PROBLEM, "not supported"); +/* + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_P_LENGTH, &fw.arp.invflags, + invert); + + getlength_and_mask(argv[optind - 1], &fw.arp.arpln, + &fw.arp.arpln_mask); + break; +*/ + + case 4:/* opcode */ + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_OPCODE, &fw.arp.invflags, + invert); + if (get16_and_mask(argv[optind - 1], &fw.arp.arpop, &fw.arp.arpop_mask, 10)) { + int i; + + for (i = 0; i < NUMOPCODES; i++) + if (!strcasecmp(opcodes[i], optarg)) + break; + if (i == NUMOPCODES) + xtables_error(PARAMETER_PROBLEM, "Problem with specified opcode"); + fw.arp.arpop = htons(i+1); + } + break; + + case 5:/* h-type */ + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_H_TYPE, &fw.arp.invflags, + invert); + if (get16_and_mask(argv[optind - 1], &fw.arp.arhrd, &fw.arp.arhrd_mask, 16)) { + if (strcasecmp(argv[optind-1], "Ethernet")) + xtables_error(PARAMETER_PROBLEM, "Problem with specified hardware type"); + fw.arp.arhrd = htons(1); + } + break; + + case 6:/* proto-type */ + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_P_TYPE, &fw.arp.invflags, + invert); + if (get16_and_mask(argv[optind - 1], &fw.arp.arpro, &fw.arp.arpro_mask, 0)) { + if (strcasecmp(argv[optind-1], "ipv4")) + xtables_error(PARAMETER_PROBLEM, "Problem with specified protocol type"); + fw.arp.arpro = htons(0x800); + } + break; + + case 'j': + set_option(&options, OPT_JUMP, &fw.arp.invflags, + invert); + jumpto = parse_target(optarg); + target = command_jump(&fw, jumpto); + break; + + case 'i': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEIN, &fw.arp.invflags, + invert); + parse_interface(argv[optind-1], + fw.arp.iniface, + fw.arp.iniface_mask); +/* fw.nfcache |= NFC_IP_IF_IN; */ + break; + + case 'o': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEOUT, &fw.arp.invflags, + invert); + parse_interface(argv[optind-1], + fw.arp.outiface, + fw.arp.outiface_mask); + /* fw.nfcache |= NFC_IP_IF_OUT; */ + break; + + case 'v': + if (!verbose) + set_option(&options, OPT_VERBOSE, + &fw.arp.invflags, invert); + verbose++; + break; + + case 'm': /*{ + size_t size; + + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --match"); + + m = find_match(optarg, LOAD_MUST_SUCCEED); + size = ARPT_ALIGN(sizeof(struct arpt_entry_match)) + + m->size; + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + opts = merge_options(opts, m->extra_opts, &m->option_offset); + }*/ + break; + + case 'n': + set_option(&options, OPT_NUMERIC, &fw.arp.invflags, + invert); + break; + + case 't': + if (invert) + xtables_error(PARAMETER_PROBLEM, + "unexpected ! flag before --table"); + *table = argv[optind-1]; + break; + + case 'V': + if (invert) + printf("Not %s ;-)\n", program_version); + else + printf("%s v%s\n", + program_name, program_version); + exit(0); + + case '0': + set_option(&options, OPT_LINENUMBERS, &fw.arp.invflags, + invert); + break; + + case 'M': + //modprobe = optarg; + break; + + case 'c': + + set_option(&options, OPT_COUNTERS, &fw.arp.invflags, + invert); + pcnt = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + bcnt = argv[optind++]; + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires packet and byte counter", + opt2char(OPT_COUNTERS)); + + if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1) + xtables_error(PARAMETER_PROBLEM, + "-%c packet counter not numeric", + opt2char(OPT_COUNTERS)); + + if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1) + xtables_error(PARAMETER_PROBLEM, + "-%c byte counter not numeric", + opt2char(OPT_COUNTERS)); + + break; + + + case 1: /* non option */ + if (optarg[0] == '!' && optarg[1] == '\0') { + if (invert) + xtables_error(PARAMETER_PROBLEM, + "multiple consecutive ! not" + " allowed"); + invert = TRUE; + optarg[0] = '\0'; + continue; + } + printf("Bad argument `%s'\n", optarg); + exit_tryhelp(2); + + default: + break; + } + invert = FALSE; + } + + if (target) + xtables_option_tfcall(target); + + /* Fix me: must put inverse options checking here --MN */ + + if (optind < argc) + xtables_error(PARAMETER_PROBLEM, + "unknown arguments found on commandline"); + if (!command) + xtables_error(PARAMETER_PROBLEM, "no command specified"); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "nothing appropriate following !"); + + if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) { + if (!(options & OPT_D_IP)) + dhostnetworkmask = "0.0.0.0/0"; + if (!(options & OPT_S_IP)) + shostnetworkmask = "0.0.0.0/0"; + } + + if (shostnetworkmask) + parse_hostnetworkmask(shostnetworkmask, &saddrs, + &(fw.arp.smsk), &nsaddrs); + + if (dhostnetworkmask) + parse_hostnetworkmask(dhostnetworkmask, &daddrs, + &(fw.arp.tmsk), &ndaddrs); + + if ((nsaddrs > 1 || ndaddrs > 1) && + (fw.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP))) + xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple" + " source or destination IP addresses"); + + if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) + xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " + "specify a unique address"); + + generic_opt_check(command, options); + + if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN) + xtables_error(PARAMETER_PROBLEM, + "chain name `%s' too long (must be under %i chars)", + chain, ARPT_FUNCTION_MAXNAMELEN); + + if (nft_init(h, xtables_arp) < 0) + xtables_error(OTHER_PROBLEM, + "Could not initialize nftables layer."); + + h->ops = nft_family_ops_lookup(h->family); + if (h->ops == NULL) + xtables_error(PARAMETER_PROBLEM, "Unknown family"); + + if (command == CMD_APPEND + || command == CMD_DELETE + || command == CMD_INSERT + || command == CMD_REPLACE) { + if (strcmp(chain, "PREROUTING") == 0 + || strcmp(chain, "INPUT") == 0) { + /* -o not valid with incoming packets. */ + if (options & OPT_VIANAMEOUT) + xtables_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEOUT), + chain); + } + + if (strcmp(chain, "POSTROUTING") == 0 + || strcmp(chain, "OUTPUT") == 0) { + /* -i not valid with outgoing packets */ + if (options & OPT_VIANAMEIN) + xtables_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEIN), + chain); + } + + if (!target && strlen(jumpto) != 0) { + size_t size; + + target = xtables_find_target(XT_STANDARD_TARGET, + XTF_LOAD_MUST_SUCCEED); + size = sizeof(struct arpt_entry_target) + target->size; + target->t = xtables_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + } + + if (!target) { + xtables_error(PARAMETER_PROBLEM, + "No target provided or" + " initalization failed"); + } + + e = generate_entry(&fw, target->t); + } + + switch (command) { + case CMD_APPEND: + ret = append_entry(h, chain, *table, e, 0, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, true); + break; + case CMD_DELETE: + /*ret = delete_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle);*/ + break; + case CMD_DELETE_NUM: + /*ret = arptc_delete_num_entry(chain, rulenum - 1, handle);*/ + break; + case CMD_REPLACE: + /*ret = replace_entry(chain, e, rulenum - 1, + saddrs, daddrs, options&OPT_VERBOSE, + handle);*/ + break; + case CMD_INSERT: + ret = append_entry(h, chain, *table, e, rulenum, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, false); + break; + case CMD_LIST: + ret = list_entries(h, chain, *table, + rulenum, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + /*options&OPT_EXPANDED*/0, + options&OPT_LINENUMBERS); + break; + case CMD_FLUSH: + ret = nft_rule_flush(h, chain, *table); + break; + case CMD_ZERO: + ret = nft_chain_zero_counters(h, chain, *table); + break; + case CMD_LIST|CMD_ZERO: + ret = list_entries(h, chain, *table, rulenum, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + /*options&OPT_EXPANDED*/0, + options&OPT_LINENUMBERS); + /*if (ret) + ret = zero_entries(chain, + options&OPT_VERBOSE, handle);*/ + break; + case CMD_NEW_CHAIN: + ret = nft_chain_user_add(h, chain, *table); + break; + case CMD_DELETE_CHAIN: + ret = nft_chain_user_del(h, chain, *table); + break; + case CMD_RENAME_CHAIN: + ret = nft_chain_user_rename(h, chain, *table, newname); + break; + case CMD_SET_POLICY: + ret = nft_chain_set(h, *table, chain, policy, NULL); + if (ret < 0) + xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n", + policy); + break; + default: + /* We should never reach this... */ + exit_tryhelp(2); + } + +/* if (verbose > 1) + dump_entries(*handle);*/ + + return ret; +} diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c index 5732ba38..5f487355 100644 --- a/iptables/xtables-multi.c +++ b/iptables/xtables-multi.c @@ -42,6 +42,7 @@ static const struct subcommand multi_subcommands[] = { {"xtables-restore", xtables_restore_main}, {"xtables-config", xtables_config_main}, {"xtables-events", xtables_events_main}, + {"xtables-arp", xtables_arp_main}, #endif {NULL}, }; diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h index c609ea5b..759e24f0 100644 --- a/iptables/xtables-multi.h +++ b/iptables/xtables-multi.h @@ -7,5 +7,6 @@ extern int xtables_save_main(int, char **); extern int xtables_restore_main(int, char **); extern int xtables_config_main(int, char **); extern int xtables_events_main(int, char **); +extern int xtables_arp_main(int, char **); #endif /* _XTABLES_MULTI_H */ -- cgit v1.2.3 From 4c4bcbcd2523da740ed02021e51cb20b14fae153 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 14 Sep 2013 20:48:39 +0200 Subject: xtables: nft-arp: fix endianess in nft_arp_parse_payload nft_arp_print_firewall already handles endianess appropriately. This fixes the output of the option that allows you to match ARP header fields. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index 09b939d0..6ec8a455 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -287,22 +287,22 @@ static void nft_arp_parse_payload(struct nft_rule_expr_iter *iter, switch (offset) { case offsetof(struct arphdr, ar_hrd): get_cmp_data(iter, &ar_hrd, sizeof(ar_hrd), &inv); - fw->arp.arhrd = htons(ar_hrd); - fw->arp.arhrd_mask = htons(0xffff); + fw->arp.arhrd = ar_hrd; + fw->arp.arhrd_mask = 0xffff; if (inv) fw->arp.invflags |= ARPT_INV_ARPHRD; break; case offsetof(struct arphdr, ar_pro): get_cmp_data(iter, &ar_pro, sizeof(ar_pro), &inv); - fw->arp.arpro = htons(ar_pro); - fw->arp.arpro_mask = htons(0xffff); + fw->arp.arpro = ar_pro; + fw->arp.arpro_mask = 0xffff; if (inv) fw->arp.invflags |= ARPT_INV_ARPPRO; break; case offsetof(struct arphdr, ar_op): get_cmp_data(iter, &ar_op, sizeof(ar_op), &inv); - fw->arp.arpop = htons(ar_op); - fw->arp.arpop_mask = htons(0xffff); + fw->arp.arpop = ar_op; + fw->arp.arpop_mask = 0xffff; if (inv) fw->arp.invflags |= ARPT_INV_ARPOP; break; -- cgit v1.2.3 From 217f021925872dcbce4187408762845ae3f6f182 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Mon, 16 Sep 2013 10:58:16 +0200 Subject: xtables: nft-arp: implements is_same op for ARP family The following patch implements the is_same operation for ARP family needed for searching arp rule. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 33 ++++++++++++++++++++++++++++++--- iptables/nft-ipv4.c | 7 +++++-- iptables/nft-ipv6.c | 7 +++++-- iptables/nft-shared.h | 4 ++-- iptables/nft.c | 31 +++++++++++++++++++++++++++++++ iptables/nft.h | 2 ++ iptables/xtables-arp.c | 2 +- 7 files changed, 76 insertions(+), 10 deletions(-) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index 6ec8a455..494d2c2f 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -15,12 +15,13 @@ #include #include -#include +#include #include #include #include "nft-shared.h" +#include "nft.h" /* a few names */ char *opcodes[] = @@ -334,7 +335,7 @@ static void nft_arp_parse_payload(struct nft_rule_expr_iter *iter, } } -static void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw) +void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw) { struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; @@ -537,9 +538,35 @@ after_devdst: fputc('\n', stdout); } +static bool nft_arp_is_same(const void *data_a, + const void *data_b) +{ + const struct arpt_entry *a = data_a; + const struct arpt_entry *b = data_b; + + if (a->arp.src.s_addr != b->arp.src.s_addr + || a->arp.tgt.s_addr != b->arp.tgt.s_addr + || a->arp.smsk.s_addr != b->arp.tmsk.s_addr + || a->arp.arpro != b->arp.arpro + || a->arp.flags != b->arp.flags + || a->arp.invflags != b->arp.invflags) { + DEBUGP("different src/dst/proto/flags/invflags\n"); + return false; + } + + return is_same_interfaces(a->arp.src_devaddr.addr, + a->arp.tgt_devaddr.addr, + (unsigned char*)a->arp.src_devaddr.mask, + (unsigned char*)a->arp.tgt_devaddr.mask, + b->arp.src_devaddr.addr, + a->arp.tgt_devaddr.addr, + (unsigned char*)b->arp.src_devaddr.mask, + (unsigned char*)b->arp.tgt_devaddr.mask); +} + struct nft_family_ops nft_family_ops_arp = { .add = nft_arp_add, - .is_same = NULL, + .is_same = nft_arp_is_same, .print_payload = NULL, .parse_meta = nft_arp_parse_meta, .parse_payload = nft_arp_parse_payload, diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 40340984..71fbc830 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -66,9 +66,12 @@ static int nft_ipv4_add(struct nft_rule *r, void *data) return cs->fw.ip.flags; } -static bool nft_ipv4_is_same(const struct iptables_command_state *a, - const struct iptables_command_state *b) +static bool nft_ipv4_is_same(const void *data_a, + const void *data_b) { + const struct iptables_command_state *a = data_a; + const struct iptables_command_state *b = data_b; + if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 2efe95e3..3d35290f 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -49,9 +49,12 @@ static int nft_ipv6_add(struct nft_rule *r, void *data) return cs->fw6.ipv6.flags; } -static bool nft_ipv6_is_same(const struct iptables_command_state *a, - const struct iptables_command_state *b) +static bool nft_ipv6_is_same(const void *data_a, + const void *data_b) { + const struct iptables_command_state *a = data_a; + const struct iptables_command_state *b = data_b; + if (memcmp(a->fw6.ipv6.src.s6_addr, b->fw6.ipv6.src.s6_addr, sizeof(struct in6_addr)) != 0 || memcmp(a->fw6.ipv6.dst.s6_addr, b->fw6.ipv6.dst.s6_addr, diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 375c714f..80f2bc6f 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -38,8 +38,8 @@ struct xtables_args; struct nft_family_ops { int (*add)(struct nft_rule *r, void *data); - bool (*is_same)(const struct iptables_command_state *a, - const struct iptables_command_state *b); + bool (*is_same)(const void *data_a, + const void *data_b); void (*print_payload)(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter); void (*parse_meta)(struct nft_rule_expr *e, uint8_t key, diff --git a/iptables/nft.c b/iptables/nft.c index 28c5473d..8ac43c55 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2660,10 +2660,41 @@ nft_arp_rule_find(struct nft_rule_list *list, const char *chain, found = true; break; } else { + struct xt_entry_target *t_fw, *t_this; + char *targname_fw, *targname_this; + struct xtables_target *target_fw, *target_this; + /* Delete by matching rule case */ nft_rule_to_arpt_entry(r, &this); DEBUGP("comparing with... "); +#ifdef DEBUG_DEL + nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0); +#endif + + if (!ops->is_same(fw, &this)) + goto next; + + t_fw = nft_arp_get_target(fw); + t_this = nft_arp_get_target(&this); + + targname_fw = t_fw->u.user.name; + targname_this = t_this->u.user.name; + + target_fw = xtables_find_target(targname_fw, XTF_TRY_LOAD); + target_this = xtables_find_target(targname_this, XTF_TRY_LOAD); + + if (target_fw != NULL && target_this != NULL) { + if (!compare_targets(target_fw, target_this)) { + DEBUGP("Different target\n"); + goto next; + } + } else { + if (strcmp(targname_fw, targname_this) != 0) { + DEBUGP("Different verdict\n"); + goto next; + } + } found = true; break; diff --git a/iptables/nft.h b/iptables/nft.h index 25275cee..09d3e0c5 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -162,4 +162,6 @@ int nft_arp_rule_insert(struct nft_handle *h, const char *chain, const char *table, struct arpt_entry *fw, int rulenum, bool verbose); +void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw); + #endif diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 68687c0e..4b3b0461 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -1402,7 +1402,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) handle);*/ break; case CMD_INSERT: - ret = append_entry(h, chain, *table, e, rulenum, + ret = append_entry(h, chain, *table, e, rulenum - 1, nsaddrs, saddrs, ndaddrs, daddrs, options&OPT_VERBOSE, false); break; -- cgit v1.2.3 From e2a2c72277b49ac611809b3978365ab3010e1597 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 18 Sep 2013 14:13:47 +0200 Subject: nft: consolidate nft_rule_find for ARP, IPv4 and IPv6 This patch kills nft_arp_rule_find, which is almost a copy and paste of the original nft_rule_find function. Refactor this function to move specific protocol parts to the corresponding nft-{ipv4,ipv6,arp}.c files. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 44 ++++++++++++ iptables/nft-ipv4.c | 9 +++ iptables/nft-ipv6.c | 9 +++ iptables/nft-shared.c | 86 ++++++++++++++++++++++ iptables/nft-shared.h | 8 +++ iptables/nft.c | 192 ++++---------------------------------------------- 6 files changed, 168 insertions(+), 180 deletions(-) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index 494d2c2f..0e6d9f98 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -564,6 +564,49 @@ static bool nft_arp_is_same(const void *data_a, (unsigned char*)b->arp.tgt_devaddr.mask); } +static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nft_rule *r, + void *data) +{ + struct arpt_entry *fw = data; + struct xt_entry_target *t_fw, *t_this; + char *targname_fw, *targname_this; + struct xtables_target *target_fw, *target_this; + struct arpt_entry this = {}; + + /* Delete by matching rule case */ + nft_rule_to_arpt_entry(r, &this); + + DEBUGP("comparing with... "); + +/* nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0); */ + + if (!ops->is_same(fw, &this)) + return false; + + t_fw = nft_arp_get_target(fw); + t_this = nft_arp_get_target(&this); + + targname_fw = t_fw->u.user.name; + targname_this = t_this->u.user.name; + + target_fw = xtables_find_target(targname_fw, XTF_TRY_LOAD); + target_this = xtables_find_target(targname_this, XTF_TRY_LOAD); + + if (target_fw != NULL && target_this != NULL) { + if (!compare_targets(target_fw, target_this)) { + DEBUGP("Different target\n"); + return false; + } + } else { + if (strcmp(targname_fw, targname_this) != 0) { + DEBUGP("Different verdict\n"); + return false; + } + } + + return true; +} + struct nft_family_ops nft_family_ops_arp = { .add = nft_arp_add, .is_same = nft_arp_is_same, @@ -573,4 +616,5 @@ struct nft_family_ops nft_family_ops_arp = { .parse_immediate = nft_arp_parse_immediate, .print_firewall = nft_arp_print_firewall, .post_parse = NULL, + .rule_find = nft_arp_rule_find, }; diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 71fbc830..2142a2e6 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -371,6 +371,14 @@ static void nft_ipv4_parse_target(struct xtables_target *t, void *data) cs->target = t; } +static bool nft_ipv4_rule_find(struct nft_family_ops *ops, + struct nft_rule *r, void *data) +{ + struct iptables_command_state *cs = data; + + return nft_ipv46_rule_find(ops, r, cs); +} + struct nft_family_ops nft_family_ops_ipv4 = { .add = nft_ipv4_add, .is_same = nft_ipv4_is_same, @@ -381,4 +389,5 @@ struct nft_family_ops nft_family_ops_ipv4 = { .save_firewall = nft_ipv4_save_firewall, .post_parse = nft_ipv4_post_parse, .parse_target = nft_ipv4_parse_target, + .rule_find = nft_ipv4_rule_find, }; diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 3d35290f..dbb148ab 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -294,6 +294,14 @@ static void nft_ipv6_parse_target(struct xtables_target *t, void *data) cs->target = t; } +static bool nft_ipv6_rule_find(struct nft_family_ops *ops, + struct nft_rule *r, void *data) +{ + struct iptables_command_state *cs = data; + + return nft_ipv46_rule_find(ops, r, cs); +} + struct nft_family_ops nft_family_ops_ipv6 = { .add = nft_ipv6_add, .is_same = nft_ipv6_is_same, @@ -304,4 +312,5 @@ struct nft_family_ops nft_family_ops_ipv6 = { .save_firewall = nft_ipv6_save_firewall, .post_parse = nft_ipv6_post_parse, .parse_target = nft_ipv6_parse_target, + .rule_find = nft_ipv6_rule_find, }; diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index c1cb5a7f..acf352d6 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -658,3 +658,89 @@ struct nft_family_ops *nft_family_ops_lookup(int family) return NULL; } +static bool +compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2) +{ + struct xtables_rule_match *mp1; + struct xtables_rule_match *mp2; + + for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) { + struct xt_entry_match *m1 = mp1->match->m; + struct xt_entry_match *m2 = mp2->match->m; + + if (strcmp(m1->u.user.name, m2->u.user.name) != 0) { + DEBUGP("mismatching match name\n"); + return false; + } + + if (m1->u.user.match_size != m2->u.user.match_size) { + DEBUGP("mismatching match size\n"); + return false; + } + + if (memcmp(m1->data, m2->data, + m1->u.user.match_size - sizeof(*m1)) != 0) { + DEBUGP("mismatch match data\n"); + return false; + } + } + + /* Both cursors should be NULL */ + if (mp1 != mp2) { + DEBUGP("mismatch matches amount\n"); + return false; + } + + return true; +} + +bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2) +{ + if (tg1 == NULL && tg2 == NULL) + return true; + + if ((tg1 == NULL && tg2 != NULL) || (tg1 != NULL && tg2 == NULL)) + return false; + + if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0) + return false; + + if (memcmp(tg1->t->data, tg2->t->data, + tg1->t->u.user.target_size - sizeof(*tg1->t)) != 0) { + return false; + } + + return true; +} + +bool nft_ipv46_rule_find(struct nft_family_ops *ops, + struct nft_rule *r, struct iptables_command_state *cs) +{ + struct iptables_command_state this = {}; + + nft_rule_to_iptables_command_state(r, &this); + + DEBUGP("comparing with... "); +#ifdef DEBUG_DEL + nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0); +#endif + if (!ops->is_same(cs, &this)) + return false; + + if (!compare_matches(cs->matches, this.matches)) { + DEBUGP("Different matches\n"); + return false; + } + + if (!compare_targets(cs->target, this.target)) { + DEBUGP("Different target\n"); + return false; + } + + if (strcmp(cs->jumpto, this.jumpto) != 0) { + DEBUGP("Different verdict\n"); + return false; + } + + return true; +} diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 80f2bc6f..3d1f433f 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -54,6 +54,8 @@ struct nft_family_ops { void (*post_parse)(int command, struct iptables_command_state *cs, struct xtables_args *args); void (*parse_target)(struct xtables_target *t, void *data); + bool (*rule_find)(struct nft_family_ops *ops, struct nft_rule *r, + void *data); }; void add_meta(struct nft_rule *r, uint32_t key); @@ -117,6 +119,12 @@ void save_firewall_details(const struct iptables_command_state *cs, struct nft_family_ops *nft_family_ops_lookup(int family); +struct nft_handle; +bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nft_rule *r, + struct iptables_command_state *cs); + +bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2); + struct addr_mask { union { struct in_addr *v4; diff --git a/iptables/nft.c b/iptables/nft.c index 8ac43c55..d08a513b 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1487,62 +1487,6 @@ next: return 0; } -static bool -compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2) -{ - struct xtables_rule_match *mp1; - struct xtables_rule_match *mp2; - - for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) { - struct xt_entry_match *m1 = mp1->match->m; - struct xt_entry_match *m2 = mp2->match->m; - - if (strcmp(m1->u.user.name, m2->u.user.name) != 0) { - DEBUGP("mismatching match name\n"); - return false; - } - - if (m1->u.user.match_size != m2->u.user.match_size) { - DEBUGP("mismatching match size\n"); - return false; - } - - if (memcmp(m1->data, m2->data, - m1->u.user.match_size - sizeof(*m1)) != 0) { - DEBUGP("mismatch match data\n"); - return false; - } - } - - /* Both cursors should be NULL */ - if (mp1 != mp2) { - DEBUGP("mismatch matches amount\n"); - return false; - } - - return true; -} - -static bool -compare_targets(struct xtables_target *tg1, struct xtables_target *tg2) -{ - if (tg1 == NULL && tg2 == NULL) - return true; - - if ((tg1 == NULL && tg2 != NULL) || (tg1 != NULL && tg2 == NULL)) - return false; - - if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0) - return false; - - if (memcmp(tg1->t->data, tg2->t->data, - tg1->t->u.user.target_size - sizeof(*tg1->t)) != 0) { - return false; - } - - return true; -} - static void __nft_rule_del(struct nft_handle *h, struct nft_rule *r) { @@ -1572,8 +1516,8 @@ void nft_rule_list_destroy(struct nft_rule_list *list) } static struct nft_rule * -nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, - struct iptables_command_state *cs, int rulenum) +nft_rule_find(struct nft_handle *h, struct nft_rule_list *list, + const char *chain, const char *table, void *data, int rulenum) { struct nft_rule *r; struct nft_rule_list_iter *iter; @@ -1590,9 +1534,6 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE); const char *rule_chain = nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); - const struct nft_family_ops *ops = nft_family_ops_lookup( - nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)); - struct iptables_command_state this = {}; if (strcmp(table, rule_table) != 0 || strcmp(chain, rule_chain) != 0) { @@ -1607,33 +1548,9 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, found = true; break; } else { - /* Delete by matching rule case */ - nft_rule_to_iptables_command_state(r, &this); - - DEBUGP("comparing with... "); -#ifdef DEBUG_DEL - nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0); -#endif - if (!ops->is_same(cs, &this)) - goto next; - - if (!compare_matches(cs->matches, this.matches)) { - DEBUGP("Different matches\n"); - goto next; - } - - if (!compare_targets(cs->target, this.target)) { - DEBUGP("Different target\n"); - goto next; - } - - if (strcmp(cs->jumpto, this.jumpto) != 0) { - DEBUGP("Different verdict\n"); - goto next; - } - - found = true; - break; + found = h->ops->rule_find(h->ops, r, data); + if (found) + break; } next: rule_ctr++; @@ -1658,7 +1575,7 @@ int nft_rule_check(struct nft_handle *h, const char *chain, if (list == NULL) return 0; - ret = nft_rule_find(list, chain, table, e, -1) ? 1 : 0; + ret = nft_rule_find(h, list, chain, table, e, -1) ? 1 : 0; if (ret == 0) errno = ENOENT; @@ -1681,7 +1598,7 @@ int nft_rule_delete(struct nft_handle *h, const char *chain, if (list == NULL) return 0; - r = nft_rule_find(list, chain, table, cs, -1); + r = nft_rule_find(h, list, chain, table, cs, -1); if (r != NULL) { ret = 1; @@ -1755,7 +1672,7 @@ int nft_rule_insert(struct nft_handle *h, const char *chain, if (list == NULL) goto err; - r = nft_rule_find(list, chain, table, cs, rulenum); + r = nft_rule_find(h, list, chain, table, cs, rulenum); if (r == NULL) { errno = ENOENT; goto err; @@ -1786,7 +1703,7 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain, if (list == NULL) return 0; - r = nft_rule_find(list, chain, table, NULL, rulenum); + r = nft_rule_find(h, list, chain, table, NULL, rulenum); if (r != NULL) { ret = 1; @@ -1818,7 +1735,7 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, if (list == NULL) return 0; - r = nft_rule_find(list, chain, table, cs, rulenum); + r = nft_rule_find(h, list, chain, table, cs, rulenum); if (r != NULL) { DEBUGP("replacing rule with handle=%llu\n", (unsigned long long) @@ -2136,7 +2053,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain, if (list == NULL) return 0; - r = nft_rule_find(list, chain, table, NULL, rulenum); + r = nft_rule_find(h, list, chain, table, NULL, rulenum); if (r == NULL) { errno = ENOENT; ret = 1; @@ -2624,91 +2541,6 @@ err: return ret == 0 ? 1 : 0; } -static struct nft_rule * -nft_arp_rule_find(struct nft_rule_list *list, const char *chain, - const char *table, struct arpt_entry *fw, int rulenum) -{ - struct nft_rule *r; - struct nft_rule_list_iter *iter; - int rule_ctr = 0; - bool found = false; - - iter = nft_rule_list_iter_create(list); - if (iter == NULL) - return 0; - - r = nft_rule_list_iter_next(iter); - while (r != NULL) { - const char *rule_table = - nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE); - const char *rule_chain = - nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); - const struct nft_family_ops *ops = nft_family_ops_lookup( - nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)); - struct arpt_entry this = {}; - - if (strcmp(table, rule_table) != 0 || - strcmp(chain, rule_chain) != 0) { - DEBUGP("different chain / table\n"); - goto next; - } - - if (rulenum >= 0) { - /* Delete by rule number case */ - if (rule_ctr != rulenum) - goto next; - found = true; - break; - } else { - struct xt_entry_target *t_fw, *t_this; - char *targname_fw, *targname_this; - struct xtables_target *target_fw, *target_this; - - /* Delete by matching rule case */ - nft_rule_to_arpt_entry(r, &this); - - DEBUGP("comparing with... "); -#ifdef DEBUG_DEL - nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0); -#endif - - if (!ops->is_same(fw, &this)) - goto next; - - t_fw = nft_arp_get_target(fw); - t_this = nft_arp_get_target(&this); - - targname_fw = t_fw->u.user.name; - targname_this = t_this->u.user.name; - - target_fw = xtables_find_target(targname_fw, XTF_TRY_LOAD); - target_this = xtables_find_target(targname_this, XTF_TRY_LOAD); - - if (target_fw != NULL && target_this != NULL) { - if (!compare_targets(target_fw, target_this)) { - DEBUGP("Different target\n"); - goto next; - } - } else { - if (strcmp(targname_fw, targname_this) != 0) { - DEBUGP("Different verdict\n"); - goto next; - } - } - - found = true; - break; - } -next: - rule_ctr++; - r = nft_rule_list_iter_next(iter); - } - - nft_rule_list_iter_destroy(iter); - - return found ? r : NULL; -} - int nft_arp_rule_insert(struct nft_handle *h, const char *chain, const char *table, struct arpt_entry *fw, int rulenum, bool verbose) @@ -2728,7 +2560,7 @@ int nft_arp_rule_insert(struct nft_handle *h, const char *chain, if (list == NULL) goto err; - r = nft_arp_rule_find(list, chain, table, fw, rulenum); + r = nft_rule_find(h, list, chain, table, fw, rulenum); if (r == NULL) { errno = ENOENT; goto err; -- cgit v1.2.3 From c6836c19592dbe1a8be9b0ad76c0ae09abcb82e7 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 18 Sep 2013 14:57:38 +0200 Subject: nft: consolidate nft_rule_new to support ARP This patch removes nft_arp_rule_new, which almost a copy and paste of the original nft_rule_new. This patch generalizes the infrastructure to support ARP. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 30 ++++++++++++- iptables/nft-ipv4.c | 15 ++++++- iptables/nft-ipv6.c | 15 ++++++- iptables/nft.c | 120 ++++++++++++++-------------------------------------- iptables/nft.h | 2 + 5 files changed, 90 insertions(+), 92 deletions(-) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index 0e6d9f98..10c7b63e 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -1,10 +1,13 @@ /* + * (C) 2013 by Pablo Neira Ayuso * (C) 2013 by Giuseppe Longo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro */ #include @@ -14,6 +17,7 @@ #include #include +#include #include #include @@ -161,6 +165,9 @@ static int nft_arp_add(struct nft_rule *r, void *data) { struct arpt_entry *fw = data; uint8_t flags = arpt_to_ipt_flags(fw->arp.invflags); + struct xt_entry_target *t; + char *targname; + int ret; if (fw->arp.iniface[0] != '\0') add_iniface(r, fw->arp.iniface, flags); @@ -207,7 +214,28 @@ static int nft_arp_add(struct nft_rule *r, void *data) add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr), &fw->arp.tgt.s_addr, 4, flags); - return 0; + /* Counters need to me added before the target, otherwise they are + * increased for each rule because of the way nf_tables works. + */ + if (add_counters(r, fw->counters.pcnt, fw->counters.bcnt) < 0) + return -1; + + t = nft_arp_get_target(fw); + targname = t->u.user.name; + + /* Standard target? */ + if (strcmp(targname, XTC_LABEL_ACCEPT) == 0) + ret = add_verdict(r, NF_ACCEPT); + else if (strcmp(targname, XTC_LABEL_DROP) == 0) + ret = add_verdict(r, NF_DROP); + else if (strcmp(targname, XTC_LABEL_RETURN) == 0) + ret = add_verdict(r, NFT_RETURN); + else if (xtables_find_target(targname, XTF_TRY_LOAD) != NULL) + ret = add_target(r, t); + else + ret = add_jumpto(r, targname, NFT_JUMP); + + return ret; } static uint16_t ipt_to_arpt_flags(uint8_t invflags) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 2142a2e6..3be801d3 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -22,11 +22,13 @@ #include +#include "nft.h" #include "nft-shared.h" static int nft_ipv4_add(struct nft_rule *r, void *data) { struct iptables_command_state *cs = data; + struct xtables_rule_match *matchp; uint32_t op; if (cs->fw.ip.iniface[0] != '\0') @@ -63,7 +65,18 @@ static int nft_ipv4_add(struct nft_rule *r, void *data) add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags); - return cs->fw.ip.flags; + for (matchp = cs->matches; matchp; matchp = matchp->next) { + if (add_match(r, matchp->match->m) < 0) + break; + } + + /* Counters need to me added before the target, otherwise they are + * increased for each rule because of the way nf_tables works. + */ + if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) + return -1; + + return add_action(r, cs, cs->fw.ip.flags); } static bool nft_ipv4_is_same(const void *data_a, diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index dbb148ab..e3784a8b 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -20,11 +20,13 @@ #include +#include "nft.h" #include "nft-shared.h" static int nft_ipv6_add(struct nft_rule *r, void *data) { struct iptables_command_state *cs = data; + struct xtables_rule_match *matchp; if (cs->fw6.ipv6.iniface[0] != '\0') add_iniface(r, cs->fw6.ipv6.iniface, cs->fw6.ipv6.invflags); @@ -46,7 +48,18 @@ static int nft_ipv6_add(struct nft_rule *r, void *data) add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags); - return cs->fw6.ipv6.flags; + for (matchp = cs->matches; matchp; matchp = matchp->next) { + if (add_match(r, matchp->match->m) < 0) + break; + } + + /* Counters need to me added before the target, otherwise they are + * increased for each rule because of the way nf_tables works. + */ + if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) + return -1; + + return add_action(r, cs, cs->fw6.ipv6.flags); } static bool nft_ipv6_is_same(const void *data_a, diff --git a/iptables/nft.c b/iptables/nft.c index d08a513b..c3784c07 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -589,7 +589,7 @@ static int __add_match(struct nft_rule_expr *e, struct xt_entry_match *m) return 0; } -static int add_match(struct nft_rule *r, struct xt_entry_match *m) +int add_match(struct nft_rule *r, struct xt_entry_match *m) { struct nft_rule_expr *expr; int ret; @@ -671,6 +671,32 @@ int add_verdict(struct nft_rule *r, int verdict) return 0; } +int add_action(struct nft_rule *r, struct iptables_command_state *cs, + int ip_flags) +{ + int ret = 0; + + /* If no target at all, add nothing (default to continue) */ + if (cs->target != NULL) { + /* Standard target? */ + if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0) + ret = add_verdict(r, NF_ACCEPT); + else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) + ret = add_verdict(r, NF_DROP); + else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0) + ret = add_verdict(r, NFT_RETURN); + else + ret = add_target(r, cs->target->t); + } else if (strlen(cs->jumpto) > 0) { + /* Not standard, then it's a go / jump to chain */ + if (ip_flags & IPT_F_GOTO) + ret = add_jumpto(r, cs->jumpto, NFT_GOTO); + else + ret = add_jumpto(r, cs->jumpto, NFT_JUMP); + } + return ret; +} + static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) { #ifdef NLDEBUG @@ -707,11 +733,9 @@ void add_compat(struct nft_rule *r, uint32_t proto, bool inv) static struct nft_rule * nft_rule_new(struct nft_handle *h, const char *chain, const char *table, - struct iptables_command_state *cs) + void *data) { struct nft_rule *r; - int ret = 0, ip_flags = 0; - struct xtables_rule_match *matchp; r = nft_rule_alloc(); if (r == NULL) @@ -721,39 +745,7 @@ nft_rule_new(struct nft_handle *h, const char *chain, const char *table, nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); - ip_flags = h->ops->add(r, cs); - - for (matchp = cs->matches; matchp; matchp = matchp->next) { - if (add_match(r, matchp->match->m) < 0) - goto err; - } - - /* Counters need to me added before the target, otherwise they are - * increased for each rule because of the way nf_tables works. - */ - if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) - goto err; - - /* If no target at all, add nothing (default to continue) */ - if (cs->target != NULL) { - /* Standard target? */ - if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0) - ret = add_verdict(r, NF_ACCEPT); - else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) - ret = add_verdict(r, NF_DROP); - else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0) - ret = add_verdict(r, NFT_RETURN); - else - ret = add_target(r, cs->target->t); - } else if (strlen(cs->jumpto) > 0) { - /* Not standard, then it's a go / jump to chain */ - if (ip_flags & IPT_F_GOTO) - ret = add_jumpto(r, cs->jumpto, NFT_GOTO); - else - ret = add_jumpto(r, cs->jumpto, NFT_JUMP); - } - - if (ret < 0) + if (h->ops->add(r, data) < 0) goto err; return r; @@ -2393,56 +2385,6 @@ err: * to ARP. These functions should go away with some refactoring of the * existing infrastructure. */ -static struct nft_rule * -nft_arp_rule_new(struct nft_handle *h, const char *chain, const char *table, - struct arpt_entry *fw) -{ - struct xt_entry_target *t; - char *targname; - struct nft_rule *r; - int ret = 0; - - t = nft_arp_get_target(fw); - targname = t->u.user.name; - - r = nft_rule_alloc(); - if (r == NULL) - return NULL; - - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, h->family); - nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); - nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); - - h->ops->add(r, fw); - - /* Counters need to me added before the target, otherwise they are - * increased for each rule because of the way nf_tables works. - */ - if (add_counters(r, fw->counters.pcnt, fw->counters.bcnt) < 0) - goto err; - - /* Standard target? */ - if (strcmp(targname, XTC_LABEL_ACCEPT) == 0) - ret = add_verdict(r, NF_ACCEPT); - else if (strcmp(targname, XTC_LABEL_DROP) == 0) - ret = add_verdict(r, NF_DROP); - else if (strcmp(targname, XTC_LABEL_RETURN) == 0) - ret = add_verdict(r, NFT_RETURN); - else if (xtables_find_target(targname, XTF_TRY_LOAD) != NULL) - ret = add_target(r, t); - else - ret = add_jumpto(r, targname, NFT_JUMP); - - if (ret < 0) - goto err; - - return r; -err: - nft_rule_free(r); - return NULL; - -} - static void nft_arp_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) { #ifdef NLDEBUG @@ -2464,7 +2406,7 @@ nft_arp_rule_add(struct nft_handle *h, const char *chain, struct nft_rule *r; int ret = 1; - r = nft_arp_rule_new(h, chain, table, fw); + r = nft_rule_new(h, chain, table, fw); if (r == NULL) { ret = 0; goto err; @@ -2507,7 +2449,7 @@ int nft_arp_rule_append(struct nft_handle *h, const char *chain, nft_fn = nft_arp_rule_append; - r = nft_arp_rule_new(h, chain, table, fw); + r = nft_rule_new(h, chain, table, fw); if (r == NULL) { ret = 0; goto err; diff --git a/iptables/nft.h b/iptables/nft.h index 09d3e0c5..8ddde48d 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -100,8 +100,10 @@ void nft_rule_list_destroy(struct nft_rule_list *list); */ int add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes); int add_verdict(struct nft_rule *r, int verdict); +int add_match(struct nft_rule *r, struct xt_entry_match *m); int add_target(struct nft_rule *r, struct xt_entry_target *t); int add_jumpto(struct nft_rule *r, const char *name, int verdict); +int add_action(struct nft_rule *r, struct iptables_command_state *cs, int ip_flags); enum nft_rule_print { NFT_RULE_APPEND, -- cgit v1.2.3 From b756cf08d6eff885d808504c674bd7eb5ebabfbb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 18 Sep 2013 16:54:15 +0200 Subject: nft: consolidate nft_rule_* functions to support ARP This should help to avoid code duplication to support ARP. As a result, we have a common generic infrastructure for IPv4, IPv6 and ARP. This patch removes nft_arp_rule_append and nft_arp_rule_insert, which were very similar to their original nft_rule_append and nft_rule_insert. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 169 ++++--------------------------------------------- iptables/nft.h | 11 ++-- iptables/xtables-arp.c | 9 ++- 3 files changed, 22 insertions(+), 167 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index c3784c07..9bf36c51 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -756,8 +756,7 @@ err: int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, - struct iptables_command_state *cs, uint64_t handle, - bool verbose) + void *data, uint64_t handle, bool verbose) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -771,7 +770,7 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table, nft_fn = nft_rule_append; - r = nft_rule_new(h, chain, table, cs); + r = nft_rule_new(h, chain, table, data); if (r == NULL) { ret = 0; goto err; @@ -1555,8 +1554,7 @@ next: } int nft_rule_check(struct nft_handle *h, const char *chain, - const char *table, struct iptables_command_state *e, - bool verbose) + const char *table, void *data, bool verbose) { struct nft_rule_list *list; int ret; @@ -1567,7 +1565,7 @@ int nft_rule_check(struct nft_handle *h, const char *chain, if (list == NULL) return 0; - ret = nft_rule_find(h, list, chain, table, e, -1) ? 1 : 0; + ret = nft_rule_find(h, list, chain, table, data, -1) ? 1 : 0; if (ret == 0) errno = ENOENT; @@ -1577,8 +1575,7 @@ int nft_rule_check(struct nft_handle *h, const char *chain, } int nft_rule_delete(struct nft_handle *h, const char *chain, - const char *table, struct iptables_command_state *cs, - bool verbose) + const char *table, void *data, bool verbose) { int ret = 0; struct nft_rule *r; @@ -1590,7 +1587,7 @@ int nft_rule_delete(struct nft_handle *h, const char *chain, if (list == NULL) return 0; - r = nft_rule_find(h, list, chain, table, cs, -1); + r = nft_rule_find(h, list, chain, table, data, -1); if (r != NULL) { ret = 1; @@ -1646,8 +1643,7 @@ err: } int nft_rule_insert(struct nft_handle *h, const char *chain, - const char *table, struct iptables_command_state *cs, - int rulenum, bool verbose) + const char *table, void *data, int rulenum, bool verbose) { struct nft_rule_list *list; struct nft_rule *r; @@ -1664,7 +1660,7 @@ int nft_rule_insert(struct nft_handle *h, const char *chain, if (list == NULL) goto err; - r = nft_rule_find(h, list, chain, table, cs, rulenum); + r = nft_rule_find(h, list, chain, table, data, rulenum); if (r == NULL) { errno = ENOENT; goto err; @@ -1676,7 +1672,7 @@ int nft_rule_insert(struct nft_handle *h, const char *chain, nft_rule_list_destroy(list); } - return nft_rule_add(h, chain, table, cs, handle, verbose); + return nft_rule_add(h, chain, table, data, handle, verbose); err: nft_rule_list_destroy(list); return 0; @@ -1714,8 +1710,7 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain, } int nft_rule_replace(struct nft_handle *h, const char *chain, - const char *table, struct iptables_command_state *cs, - int rulenum, bool verbose) + const char *table, void *data, int rulenum, bool verbose) { int ret = 0; struct nft_rule *r; @@ -1727,7 +1722,7 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, if (list == NULL) return 0; - r = nft_rule_find(h, list, chain, table, cs, rulenum); + r = nft_rule_find(h, list, chain, table, data, rulenum); if (r != NULL) { DEBUGP("replacing rule with handle=%llu\n", (unsigned long long) @@ -1737,7 +1732,7 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, NFT_RULE_F_COMMIT); } - ret = nft_rule_append(h, chain, table, cs, + ret = nft_rule_append(h, chain, table, data, nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE), verbose); } else @@ -2379,143 +2374,3 @@ err: /* the core expects 1 for success and 0 for error */ return ret == 0 ? 1 : 0; } - -/* - * XXX These are almost copy and paste of the original functions, but adapted - * to ARP. These functions should go away with some refactoring of the - * existing infrastructure. - */ -static void -nft_arp_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) { -#ifdef NLDEBUG - char tmp[1024]; - - nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0); - printf("DEBUG: rule: %s", tmp); - mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg)); -#endif -} - -static int -nft_arp_rule_add(struct nft_handle *h, const char *chain, - const char *table, struct arpt_entry *fw, - uint64_t handle, bool verbose) -{ - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; - struct nft_rule *r; - int ret = 1; - - r = nft_rule_new(h, chain, table, fw); - if (r == NULL) { - ret = 0; - goto err; - } - if (handle > 0) - nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle); - - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); - } - - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, - NLM_F_ACK|NLM_F_CREATE, h->seq); - nft_rule_nlmsg_build_payload(nlh, r); - nft_arp_rule_print_debug(r, nlh); - nft_rule_free(r); - - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) - perror("mnl_talk:nft_arp_rule_add"); - -err: - /* the core expects 1 for success and 0 for error */ - return ret == 0 ? 1 : 0; -} - -int nft_arp_rule_append(struct nft_handle *h, const char *chain, - const char *table, struct arpt_entry *fw, bool verbose) -{ - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; - struct nft_rule *r; - uint16_t flags = NLM_F_ACK|NLM_F_CREATE; - int ret = 1; - - /* If built-in chains don't exist for this table, create them */ - if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) - nft_chain_builtin_init(h, table, chain, NF_ACCEPT); - - nft_fn = nft_arp_rule_append; - - r = nft_rule_new(h, chain, table, fw); - if (r == NULL) { - ret = 0; - goto err; - } - - /*if (handle > 0) { - nft_rule_attr_set(r, NFT_RULE_ATTR_HANDLE, &handle); - flags |= NLM_F_REPLACE; - } else - flags |= NLM_F_APPEND;*/ - - flags |= NLM_F_APPEND; - - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); - } - - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, - flags, h->seq); - nft_rule_nlmsg_build_payload(nlh, r); - nft_arp_rule_print_debug(r, nlh); - nft_rule_free(r); - - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) - perror("mnl_talk:nft_arp_rule_append"); - -err: - /* the core expects 1 for success and 0 for error */ - return ret == 0 ? 1 : 0; -} - -int nft_arp_rule_insert(struct nft_handle *h, const char *chain, - const char *table, struct arpt_entry *fw, - int rulenum, bool verbose) -{ - struct nft_rule_list *list; - struct nft_rule *r; - uint64_t handle = 0; - - /* If built-in chains don't exist for this table, create them */ - if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) - nft_chain_builtin_init(h, table, chain, NF_ACCEPT); - - nft_fn = nft_arp_rule_insert; - - if (rulenum > 0) { - list = nft_rule_list_create(h); - if (list == NULL) - goto err; - - r = nft_rule_find(h, list, chain, table, fw, rulenum); - if (r == NULL) { - errno = ENOENT; - goto err; - } - - handle = nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE); - DEBUGP("adding after rule handle %"PRIu64"\n", handle); - - nft_rule_list_destroy(list); - } - - return nft_arp_rule_add(h, chain, table, fw, handle, verbose); -err: - nft_rule_list_destroy(list); - return 0; -} diff --git a/iptables/nft.h b/iptables/nft.h index 8ddde48d..50971e86 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -81,17 +81,18 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char */ struct nft_rule; -int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, uint64_t handle, bool verbose); -int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, int rulenum, bool verbose); -int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); -int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose); +int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, void *data, uint64_t handle, bool verbose); +int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, void *data, int rulenum, bool verbose); +int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose); +int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose); int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose); -int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, int rulenum, bool verbose); +int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, void *data, int rulenum, bool verbose); int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format); int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *table, int rulenum, int counters); int nft_rule_save(struct nft_handle *h, const char *table, bool counters); int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table); int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *table, int rulenum); + struct nft_rule_list *nft_rule_list_create(struct nft_handle *h); void nft_rule_list_destroy(struct nft_rule_list *list); diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 4b3b0461..91876174 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -884,12 +884,11 @@ append_entry(struct nft_handle *h, for (j = 0; j < ndaddrs; j++) { fw->arp.tgt.s_addr = daddrs[j].s_addr; if (append) { - ret = nft_arp_rule_append(h, chain, table, - fw, verbose); + ret = nft_rule_append(h, chain, table, fw, 0, + verbose); } else { - ret = nft_arp_rule_insert(h, chain, table, - fw, rulenum, - verbose); + ret = nft_rule_insert(h, chain, table, fw, + rulenum, verbose); } } } -- cgit v1.2.3 From ed8247182d45f6208f86dd87d12e6239e0e54909 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Wed, 18 Sep 2013 12:34:15 +0200 Subject: xtables: arp: add rule replacement support The following patch permit to replace rule in xtables-arp. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-arp.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 91876174..8dfdf63e 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -734,7 +734,7 @@ parse_rulenumber(const char *rule) { unsigned int rulenum; - if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1) + if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX)) xtables_error(PARAMETER_PROBLEM, "Invalid rule number `%s'", rule); @@ -896,6 +896,21 @@ append_entry(struct nft_handle *h, return ret; } +static int +replace_entry(const char *chain, + const char *table, + struct arpt_entry *fw, + unsigned int rulenum, + const struct in_addr *saddr, + const struct in_addr *daddr, + bool verbose, struct nft_handle *h) +{ + fw->arp.src.s_addr = saddr->s_addr; + fw->arp.tgt.s_addr = daddr->s_addr; + + return nft_rule_replace(h, chain, table, fw, rulenum, verbose); +} + int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) { struct arpt_entry fw, *e = NULL; @@ -1396,9 +1411,8 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) /*ret = arptc_delete_num_entry(chain, rulenum - 1, handle);*/ break; case CMD_REPLACE: - /*ret = replace_entry(chain, e, rulenum - 1, - saddrs, daddrs, options&OPT_VERBOSE, - handle);*/ + ret = replace_entry(chain, *table, e, rulenum - 1, + saddrs, daddrs, options&OPT_VERBOSE, h); break; case CMD_INSERT: ret = append_entry(h, chain, *table, e, rulenum - 1, -- cgit v1.2.3 From b8635aea998c4d52e38f7b20cbd4afa898ef2cc7 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 18 Sep 2013 19:01:11 +0200 Subject: include: cache netfilter_arp kernel headers To ensure that compilation does not break if Linux kernel headers are not installed in the system. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_arp.h | 19 +++ include/linux/netfilter_arp/arp_tables.h | 204 ++++++++++++++++++++++++++++++ include/linux/netfilter_arp/arpt_mangle.h | 26 ++++ 3 files changed, 249 insertions(+) create mode 100644 include/linux/netfilter_arp.h create mode 100644 include/linux/netfilter_arp/arp_tables.h create mode 100644 include/linux/netfilter_arp/arpt_mangle.h diff --git a/include/linux/netfilter_arp.h b/include/linux/netfilter_arp.h new file mode 100644 index 00000000..92bc6ddc --- /dev/null +++ b/include/linux/netfilter_arp.h @@ -0,0 +1,19 @@ +#ifndef __LINUX_ARP_NETFILTER_H +#define __LINUX_ARP_NETFILTER_H + +/* ARP-specific defines for netfilter. + * (C)2002 Rusty Russell IBM -- This code is GPL. + */ + +#include + +/* There is no PF_ARP. */ +#define NF_ARP 0 + +/* ARP Hooks */ +#define NF_ARP_IN 0 +#define NF_ARP_OUT 1 +#define NF_ARP_FORWARD 2 +#define NF_ARP_NUMHOOKS 3 + +#endif /* __LINUX_ARP_NETFILTER_H */ diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h new file mode 100644 index 00000000..bb1ec648 --- /dev/null +++ b/include/linux/netfilter_arp/arp_tables.h @@ -0,0 +1,204 @@ +/* + * Format of an ARP firewall descriptor + * + * src, tgt, src_mask, tgt_mask, arpop, arpop_mask are always stored in + * network byte order. + * flags are stored in host byte order (of course). + */ + +#ifndef _ARPTABLES_H +#define _ARPTABLES_H + +#include + +#include + +#include + +#define ARPT_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN +#define ARPT_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN +#define arpt_entry_target xt_entry_target +#define arpt_standard_target xt_standard_target +#define arpt_error_target xt_error_target +#define ARPT_CONTINUE XT_CONTINUE +#define ARPT_RETURN XT_RETURN +#define arpt_counters_info xt_counters_info +#define arpt_counters xt_counters +#define ARPT_STANDARD_TARGET XT_STANDARD_TARGET +#define ARPT_ERROR_TARGET XT_ERROR_TARGET +#define ARPT_ENTRY_ITERATE(entries, size, fn, args...) \ + XT_ENTRY_ITERATE(struct arpt_entry, entries, size, fn, ## args) + +#define ARPT_DEV_ADDR_LEN_MAX 16 + +struct arpt_devaddr_info { + char addr[ARPT_DEV_ADDR_LEN_MAX]; + char mask[ARPT_DEV_ADDR_LEN_MAX]; +}; + +/* Yes, Virginia, you have to zero the padding. */ +struct arpt_arp { + /* Source and target IP addr */ + struct in_addr src, tgt; + /* Mask for src and target IP addr */ + struct in_addr smsk, tmsk; + + /* Device hw address length, src+target device addresses */ + __u8 arhln, arhln_mask; + struct arpt_devaddr_info src_devaddr; + struct arpt_devaddr_info tgt_devaddr; + + /* ARP operation code. */ + __be16 arpop, arpop_mask; + + /* ARP hardware address and protocol address format. */ + __be16 arhrd, arhrd_mask; + __be16 arpro, arpro_mask; + + /* The protocol address length is only accepted if it is 4 + * so there is no use in offering a way to do filtering on it. + */ + + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + + /* Flags word */ + __u8 flags; + /* Inverse flags */ + __u16 invflags; +}; + +/* Values for "flag" field in struct arpt_ip (general arp structure). + * No flags defined yet. + */ +#define ARPT_F_MASK 0x00 /* All possible flag bits mask. */ + +/* Values for "inv" field in struct arpt_arp. */ +#define ARPT_INV_VIA_IN 0x0001 /* Invert the sense of IN IFACE. */ +#define ARPT_INV_VIA_OUT 0x0002 /* Invert the sense of OUT IFACE */ +#define ARPT_INV_SRCIP 0x0004 /* Invert the sense of SRC IP. */ +#define ARPT_INV_TGTIP 0x0008 /* Invert the sense of TGT IP. */ +#define ARPT_INV_SRCDEVADDR 0x0010 /* Invert the sense of SRC DEV ADDR. */ +#define ARPT_INV_TGTDEVADDR 0x0020 /* Invert the sense of TGT DEV ADDR. */ +#define ARPT_INV_ARPOP 0x0040 /* Invert the sense of ARP OP. */ +#define ARPT_INV_ARPHRD 0x0080 /* Invert the sense of ARP HRD. */ +#define ARPT_INV_ARPPRO 0x0100 /* Invert the sense of ARP PRO. */ +#define ARPT_INV_ARPHLN 0x0200 /* Invert the sense of ARP HLN. */ +#define ARPT_INV_MASK 0x03FF /* All possible flag bits mask. */ + +/* This structure defines each of the firewall rules. Consists of 3 + parts which are 1) general ARP header stuff 2) match specific + stuff 3) the target to perform if the rule matches */ +struct arpt_entry +{ + struct arpt_arp arp; + + /* Size of arpt_entry + matches */ + __u16 target_offset; + /* Size of arpt_entry + matches + target */ + __u16 next_offset; + + /* Back pointer */ + unsigned int comefrom; + + /* Packet and byte counters. */ + struct xt_counters counters; + + /* The matches (if any), then the target. */ + unsigned char elems[0]; +}; + +/* + * New IP firewall options for [gs]etsockopt at the RAW IP level. + * Unlike BSD Linux inherits IP options so you don't have to use a raw + * socket for this. Instead we check rights in the calls. + * + * ATTENTION: check linux/in.h before adding new number here. + */ +#define ARPT_BASE_CTL 96 + +#define ARPT_SO_SET_REPLACE (ARPT_BASE_CTL) +#define ARPT_SO_SET_ADD_COUNTERS (ARPT_BASE_CTL + 1) +#define ARPT_SO_SET_MAX ARPT_SO_SET_ADD_COUNTERS + +#define ARPT_SO_GET_INFO (ARPT_BASE_CTL) +#define ARPT_SO_GET_ENTRIES (ARPT_BASE_CTL + 1) +/* #define ARPT_SO_GET_REVISION_MATCH (APRT_BASE_CTL + 2) */ +#define ARPT_SO_GET_REVISION_TARGET (ARPT_BASE_CTL + 3) +#define ARPT_SO_GET_MAX (ARPT_SO_GET_REVISION_TARGET) + +/* The argument to ARPT_SO_GET_INFO */ +struct arpt_getinfo { + /* Which table: caller fills this in. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* Kernel fills these in. */ + /* Which hook entry points are valid: bitmask */ + unsigned int valid_hooks; + + /* Hook entry points: one per netfilter hook. */ + unsigned int hook_entry[NF_ARP_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_ARP_NUMHOOKS]; + + /* Number of entries */ + unsigned int num_entries; + + /* Size of entries. */ + unsigned int size; +}; + +/* The argument to ARPT_SO_SET_REPLACE. */ +struct arpt_replace { + /* Which table. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* Which hook entry points are valid: bitmask. You can't + change this. */ + unsigned int valid_hooks; + + /* Number of entries */ + unsigned int num_entries; + + /* Total size of new entries */ + unsigned int size; + + /* Hook entry points. */ + unsigned int hook_entry[NF_ARP_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_ARP_NUMHOOKS]; + + /* Information about old entries: */ + /* Number of counters (must be equal to current number of entries). */ + unsigned int num_counters; + /* The old entries' counters. */ + struct xt_counters *counters; + + /* The entries (hang off end: not really an array). */ + struct arpt_entry entries[0]; +}; + +/* The argument to ARPT_SO_GET_ENTRIES. */ +struct arpt_get_entries { + /* Which table: user fills this in. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* User fills this in: total entry size. */ + unsigned int size; + + /* The entries. */ + struct arpt_entry entrytable[0]; +}; + +/* Helper functions */ +static __inline__ struct xt_entry_target *arpt_get_target(struct arpt_entry *e) +{ + return (void *)e + e->target_offset; +} + +/* + * Main firewall chains definitions and global var's definitions. + */ +#endif /* _ARPTABLES_H */ diff --git a/include/linux/netfilter_arp/arpt_mangle.h b/include/linux/netfilter_arp/arpt_mangle.h new file mode 100644 index 00000000..250f5029 --- /dev/null +++ b/include/linux/netfilter_arp/arpt_mangle.h @@ -0,0 +1,26 @@ +#ifndef _ARPT_MANGLE_H +#define _ARPT_MANGLE_H +#include + +#define ARPT_MANGLE_ADDR_LEN_MAX sizeof(struct in_addr) +struct arpt_mangle +{ + char src_devaddr[ARPT_DEV_ADDR_LEN_MAX]; + char tgt_devaddr[ARPT_DEV_ADDR_LEN_MAX]; + union { + struct in_addr src_ip; + } u_s; + union { + struct in_addr tgt_ip; + } u_t; + u_int8_t flags; + int target; +}; + +#define ARPT_MANGLE_SDEV 0x01 +#define ARPT_MANGLE_TDEV 0x02 +#define ARPT_MANGLE_SIP 0x04 +#define ARPT_MANGLE_TIP 0x08 +#define ARPT_MANGLE_MASK 0x0f + +#endif /* _ARPT_MANGLE_H */ -- cgit v1.2.3 From 7c7dcb2f2b86f71578c4cfc810042c98a43ea70a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 19 Sep 2013 20:46:42 +0200 Subject: nft: adapt nft_rule_expr_get to use uint32_t instead of size_t According to libnftables change 437d610, now the length obtained via getter function is uint32_t, not size_t anymore. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index acf352d6..5c09fe1d 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -223,7 +223,7 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, { uint32_t value; const void *ifname; - size_t len; + uint32_t len; switch(key) { case NFT_META_IIF: @@ -285,7 +285,7 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, void nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, void *data) { - size_t tg_len; + uint32_t tg_len; const char *targname = nft_rule_expr_get_str(e, NFT_EXPR_TG_NAME); const void *targinfo = nft_rule_expr_get(e, NFT_EXPR_TG_INFO, &tg_len); struct xtables_target *target; @@ -316,7 +316,7 @@ static void nft_parse_match(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, struct iptables_command_state *cs) { - size_t mt_len; + uint32_t mt_len; const char *mt_name = nft_rule_expr_get_str(e, NFT_EXPR_MT_NAME); const void *mt_info = nft_rule_expr_get(e, NFT_EXPR_MT_INFO, &mt_len); struct xtables_match *match; @@ -360,7 +360,7 @@ void get_cmp_data(struct nft_rule_expr_iter *iter, { struct nft_rule_expr *e; const char *name; - size_t len; + uint32_t len; uint8_t op; e = nft_rule_expr_iter_next(iter); -- cgit v1.2.3 From 2a55b8114a23c36e003c0b5bc384497bc2285369 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Sun, 22 Sep 2013 10:18:55 +0200 Subject: xtables: arp: add delete operation The following patch permit to delete the rules specifying an entry or a rule number. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-arp.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 8dfdf63e..4537a58c 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -911,6 +911,30 @@ replace_entry(const char *chain, return nft_rule_replace(h, chain, table, fw, rulenum, verbose); } +static int +delete_entry(const char *chain, + const char *table, + struct arpt_entry *fw, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + bool verbose, struct nft_handle *h) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->arp.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->arp.tgt.s_addr = daddrs[j].s_addr; + ret = nft_rule_delete(h, chain, table, fw, verbose); + } + } + + return ret; +} + int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) { struct arpt_entry fw, *e = NULL; @@ -1402,13 +1426,12 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) options&OPT_VERBOSE, true); break; case CMD_DELETE: - /*ret = delete_entry(chain, e, - nsaddrs, saddrs, ndaddrs, daddrs, - options&OPT_VERBOSE, - handle);*/ + ret = delete_entry(chain, *table, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, h); break; case CMD_DELETE_NUM: - /*ret = arptc_delete_num_entry(chain, rulenum - 1, handle);*/ + ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose); break; case CMD_REPLACE: ret = replace_entry(chain, *table, e, rulenum - 1, -- cgit v1.2.3 From dbc05560e73d6df7f7013fa892810b9b8329aa1d Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Sun, 22 Sep 2013 10:18:56 +0200 Subject: xtables: arp: zeroing chain counters This small patch permit to reset the chain counters. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-arp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 4537a58c..407fb066 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -1462,9 +1462,8 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) options&OPT_NUMERIC, /*options&OPT_EXPANDED*/0, options&OPT_LINENUMBERS); - /*if (ret) - ret = zero_entries(chain, - options&OPT_VERBOSE, handle);*/ + if (ret) + ret = nft_chain_zero_counters(h, chain, *table); break; case CMD_NEW_CHAIN: ret = nft_chain_user_add(h, chain, *table); -- cgit v1.2.3 From d6a127cd5710f8c60e95bfd0378ca352c07140a9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 18 Sep 2013 17:00:18 +0200 Subject: xtables: batch rule-set updates into one single netlink message With this patch, all rule-set updates are put in one single batch of netlink messages that is sent to user-space using the new nfnetlink batch infrastructure. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 7 - include/linux/netfilter/nfnetlink.h | 44 +--- iptables/nft.c | 429 ++++++++++++++++++++++++++---------- iptables/nft.h | 5 +- iptables/xtables-arp-standalone.c | 2 + iptables/xtables-restore.c | 1 - iptables/xtables-standalone.c | 3 + 7 files changed, 328 insertions(+), 163 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 5f40dc05..74c5f4ed 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -35,8 +35,6 @@ enum nf_tables_msg_types { NFT_MSG_NEWSETELEM, NFT_MSG_GETSETELEM, NFT_MSG_DELSETELEM, - NFT_MSG_COMMIT, - NFT_MSG_ABORT, NFT_MSG_MAX, }; @@ -85,11 +83,6 @@ enum nft_chain_attributes { }; #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) -enum { - NFT_RULE_F_COMMIT = (1 << 0), - NFT_RULE_F_MASK = NFT_RULE_F_COMMIT, -}; - enum nft_rule_attributes { NFTA_RULE_UNSPEC, NFTA_RULE_TABLE, diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 91eebab4..06eea26b 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -57,44 +57,8 @@ struct nfgenmsg { #define NFNL_SUBSYS_NFT_COMPAT 11 #define NFNL_SUBSYS_COUNT 12 -#ifdef __KERNEL__ +/* Reserved control nfnetlink messages */ +#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE +#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1 -#include -#include -#include - -struct nfnl_callback { - int (*call)(struct sock *nl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const cda[]); - int (*call_rcu)(struct sock *nl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const cda[]); - const struct nla_policy *policy; /* netlink attribute policy */ - const u_int16_t attr_count; /* number of nlattr's */ -}; - -struct nfnetlink_subsystem { - const char *name; - __u8 subsys_id; /* nfnetlink subsystem ID */ - __u8 cb_count; /* number of callbacks */ - const struct nfnl_callback *cb; /* callback for individual types */ -}; - -extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); -extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); - -extern int nfnetlink_has_listeners(struct net *net, unsigned int group); -extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, - int echo, gfp_t flags); -extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); -extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); - -extern void nfnl_lock(void); -extern void nfnl_unlock(void); - -#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ - MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) - -#endif /* __KERNEL__ */ -#endif /* _NFNETLINK_H */ +#endif /* _NFNETLINK_H */ diff --git a/iptables/nft.c b/iptables/nft.c index 9bf36c51..50e756be 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -80,6 +80,195 @@ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, return 0; } +static LIST_HEAD(batch_page_list); +static int batch_num_pages; + +struct batch_page { + struct list_head head; + struct mnl_nlmsg_batch *batch; +}; + +/* selected batch page is 256 Kbytes long to load ruleset of + * half a million rules without hitting -EMSGSIZE due to large + * iovec. + */ +#define BATCH_PAGE_SIZE getpagesize() * 32 + +static struct mnl_nlmsg_batch *mnl_nft_batch_alloc(void) +{ + static char *buf; + + /* libmnl needs higher buffer to handle batch overflows */ + buf = malloc(BATCH_PAGE_SIZE + getpagesize()); + if (buf == NULL) + return NULL; + + return mnl_nlmsg_batch_start(buf, BATCH_PAGE_SIZE); +} + +static struct mnl_nlmsg_batch * +mnl_nft_batch_page_add(struct mnl_nlmsg_batch *batch) +{ + struct batch_page *batch_page; + + batch_page = malloc(sizeof(struct batch_page)); + if (batch_page == NULL) + return NULL; + + batch_page->batch = batch; + list_add_tail(&batch_page->head, &batch_page_list); + batch_num_pages++; + + return mnl_nft_batch_alloc(); +} + +static int nlbuffsiz; + +static void mnl_nft_set_sndbuffer(const struct mnl_socket *nl) +{ + int newbuffsiz; + + if (batch_num_pages * BATCH_PAGE_SIZE <= nlbuffsiz) + return; + + newbuffsiz = batch_num_pages * BATCH_PAGE_SIZE; + + /* Rise sender buffer length to avoid hitting -EMSGSIZE */ + if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE, + &newbuffsiz, sizeof(socklen_t)) < 0) + return; + + nlbuffsiz = newbuffsiz; +} + +static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl) +{ + static const struct sockaddr_nl snl = { + .nl_family = AF_NETLINK + }; + struct iovec iov[batch_num_pages]; + struct msghdr msg = { + .msg_name = (struct sockaddr *) &snl, + .msg_namelen = sizeof(snl), + .msg_iov = iov, + .msg_iovlen = batch_num_pages, + }; + struct batch_page *batch_page, *next; + int i = 0; + + mnl_nft_set_sndbuffer(nl); + + list_for_each_entry_safe(batch_page, next, &batch_page_list, head) { + iov[i].iov_base = mnl_nlmsg_batch_head(batch_page->batch); + iov[i].iov_len = mnl_nlmsg_batch_size(batch_page->batch); + i++; +#ifdef NL_DEBUG + mnl_nlmsg_fprintf(stdout, + mnl_nlmsg_batch_head(batch_page->batch), + mnl_nlmsg_batch_size(batch_page->batch), + sizeof(struct nfgenmsg)); +#endif + list_del(&batch_page->head); + free(batch_page->batch); + free(batch_page); + batch_num_pages--; + } + + return sendmsg(mnl_socket_get_fd(nl), &msg, 0); +} + +static int cb_err(const struct nlmsghdr *nlh, void *data) +{ + /* We can provide better error reporting than iptables-restore */ + errno = EINVAL; + return MNL_CB_ERROR; +} + +static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = { + [NLMSG_ERROR] = cb_err, +}; + +static int mnl_nft_batch_talk(struct nft_handle *h) +{ + int ret, fd = mnl_socket_get_fd(h->nl); + char rcv_buf[MNL_SOCKET_BUFFER_SIZE]; + fd_set readfds; + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 0 + }; + int err = 0; + + ret = mnl_nft_socket_sendmsg(h->nl); + if (ret == -1) { + perror("mnl_socket_sendmsg"); + return -1; + } + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + /* receive and digest all the acknowledgments from the kernel. */ + ret = select(fd+1, &readfds, NULL, NULL, &tv); + if (ret == -1) { + perror("select"); + return -1; + } + while (ret > 0 && FD_ISSET(fd, &readfds)) { + ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return -1; + } + + ret = mnl_cb_run2(rcv_buf, ret, 0, h->portid, + NULL, NULL, cb_ctl_array, + MNL_ARRAY_SIZE(cb_ctl_array)); + /* Continue on error, make sure we get all acknoledgments */ + if (ret == -1) + err = errno; + + ret = select(fd+1, &readfds, NULL, NULL, &tv); + if (ret == -1) { + perror("select"); + return -1; + } + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + } + return err ? -1 : 0; +} + +static void mnl_nft_batch_put(struct mnl_nlmsg_batch *batch, int type, + uint32_t seq) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfg; + + nlh = mnl_nlmsg_put_header(mnl_nlmsg_batch_current(batch)); + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_seq = seq; + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = NFNL_SUBSYS_NFTABLES; + + if (!mnl_nlmsg_batch_next(batch)) + mnl_nft_batch_page_add(batch); +} + +static void mnl_nft_batch_begin(struct mnl_nlmsg_batch *batch, uint32_t seq) +{ + mnl_nft_batch_put(batch, NFNL_MSG_BATCH_BEGIN, seq); +} + +static void mnl_nft_batch_end(struct mnl_nlmsg_batch *batch, uint32_t seq) +{ + mnl_nft_batch_put(batch, NFNL_MSG_BATCH_END, seq); +} + struct builtin_table xtables_ipv4[TABLES_MAX] = { [RAW] = { .name = "raw", @@ -419,6 +608,10 @@ int nft_init(struct nft_handle *h, struct builtin_table *t) h->portid = mnl_socket_get_portid(h->nl); h->tables = t; + INIT_LIST_HEAD(&h->rule_list); + + h->batch = mnl_nft_batch_alloc(); + return 0; } @@ -754,15 +947,45 @@ err: return NULL; } +enum rule_update_type { + NFT_DO_APPEND, + NFT_DO_INSERT, + NFT_DO_REPLACE, + NFT_DO_DELETE, + NFT_DO_FLUSH, + NFT_DO_COMMIT, + NFT_DO_ABORT, +}; + +struct rule_update { + struct list_head head; + enum rule_update_type type; + struct nft_rule *rule; +}; + +static int rule_update_add(struct nft_handle *h, enum rule_update_type type, + struct nft_rule *r) +{ + struct rule_update *rupd; + + rupd = calloc(1, sizeof(struct rule_update)); + if (rupd == NULL) + return -1; + + rupd->rule = r; + rupd->type = type; + list_add_tail(&rupd->head, &h->rule_list); + h->rule_list_num++; + + return 0; +} + int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, void *data, uint64_t handle, bool verbose) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; struct nft_rule *r; - uint16_t flags = NLM_F_ACK|NLM_F_CREATE; - int ret = 1; + int type; /* If built-in chains don't exist for this table, create them */ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) @@ -771,37 +994,19 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table, nft_fn = nft_rule_append; r = nft_rule_new(h, chain, table, data); - if (r == NULL) { - ret = 0; - goto err; - } + if (r == NULL) + return 0; if (handle > 0) { nft_rule_attr_set(r, NFT_RULE_ATTR_HANDLE, &handle); - flags |= NLM_F_REPLACE; + type = NFT_DO_REPLACE; } else - flags |= NLM_F_APPEND; - - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); - } - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, - flags, h->seq); - - nft_rule_nlmsg_build_payload(nlh, r); + type = NFT_DO_APPEND; - nft_rule_print_debug(r, nlh); - - nft_rule_free(r); - - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) - perror("mnl_talk:nft_rule_append"); + if (rule_update_add(h, type, r) < 0) + nft_rule_free(r); -err: - /* the core expects 1 for success and 0 for error */ - return ret == 0 ? 1 : 0; + return 1; } void @@ -1056,8 +1261,6 @@ next: static void __nft_rule_flush(struct nft_handle *h, const char *table, const char *chain) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; struct nft_rule *r; r = nft_rule_alloc(); @@ -1067,21 +1270,8 @@ __nft_rule_flush(struct nft_handle *h, const char *table, const char *chain) nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); - } - - /* Delete all rules in this table + chain */ - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family, - NLM_F_ACK, h->seq); - nft_rule_nlmsg_build_payload(nlh, r); - nft_rule_free(r); - - if (mnl_talk(h, nlh, NULL, NULL) < 0) { - if (errno != EEXIST) - perror("mnl_talk:__nft_rule_flush"); - } + if (rule_update_add(h, NFT_DO_FLUSH, r) < 0) + nft_rule_free(r); } int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table) @@ -1478,22 +1668,19 @@ next: return 0; } -static void -__nft_rule_del(struct nft_handle *h, struct nft_rule *r) +static int __nft_rule_del(struct nft_handle *h, struct nft_rule_list *list, + struct nft_rule *r) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; int ret; - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family, - NLM_F_ACK, h->seq); - nft_rule_nlmsg_build_payload(nlh, r); - - nft_rule_print_debug(r, nlh); + nft_rule_list_del(r); - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) - perror("mnl_talk:nft_rule_del"); + ret = rule_update_add(h, NFT_DO_DELETE, r); + if (ret < 0) { + nft_rule_free(r); + return -1; + } + return 1; } struct nft_rule_list *nft_rule_list_create(struct nft_handle *h) @@ -1589,14 +1776,9 @@ int nft_rule_delete(struct nft_handle *h, const char *chain, r = nft_rule_find(h, list, chain, table, data, -1); if (r != NULL) { - ret = 1; - - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); - } - DEBUGP("deleting rule\n"); - __nft_rule_del(h, r); + ret =__nft_rule_del(h, list, r); + if (ret < 0) + errno = ENOMEM; } else errno = ENOENT; @@ -1610,36 +1792,21 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs, uint64_t handle, bool verbose) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; struct nft_rule *r; - int ret = 1; r = nft_rule_new(h, chain, table, cs); - if (r == NULL) { - ret = 0; - goto err; - } + if (r == NULL) + return 0; + if (handle > 0) nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle); - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); + if (rule_update_add(h, NFT_DO_INSERT, r) < 0) { + nft_rule_free(r); + return 0; } - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, - NLM_F_ACK|NLM_F_CREATE, h->seq); - nft_rule_nlmsg_build_payload(nlh, r); - nft_rule_print_debug(r, nlh); - nft_rule_free(r); - - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) - perror("mnl_talk:nft_rule_add_num"); -err: - /* the core expects 1 for success and 0 for error */ - return ret == 0 ? 1 : 0; + return 1; } int nft_rule_insert(struct nft_handle *h, const char *chain, @@ -1695,12 +1862,10 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain, if (r != NULL) { ret = 1; - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); - } DEBUGP("deleting rule by number %d\n", rulenum); - __nft_rule_del(h, r); + ret = __nft_rule_del(h, list, r); + if (ret < 0) + errno = ENOMEM; } else errno = ENOENT; @@ -1728,10 +1893,6 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, (unsigned long long) nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE)); - if (h->commit) { - nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, - NFT_RULE_F_COMMIT); - } ret = nft_rule_append(h, chain, table, data, nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE), verbose); @@ -2061,39 +2222,79 @@ error: return ret; } -static int nft_action(struct nft_handle *h, int type) +static int nft_action(struct nft_handle *h, int action) { - char buf[MNL_SOCKET_BUFFER_SIZE]; + int flags = NLM_F_CREATE, type; + struct rule_update *n, *tmp; struct nlmsghdr *nlh; - uint32_t seq; + uint32_t seq = 1; int ret; - nlh = mnl_nlmsg_put_header(buf); - nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES<< 8) | type; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - nlh->nlmsg_seq = seq = time(NULL); + mnl_nft_batch_begin(h->batch, seq++); - struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); - nfg->nfgen_family = h->family; - nfg->version = NFNETLINK_V0; - nfg->res_id = 0; + list_for_each_entry_safe(n, tmp, &h->rule_list, head) { + switch (n->type) { + case NFT_DO_APPEND: + type = NFT_MSG_NEWRULE; + flags |= NLM_F_APPEND; + break; + case NFT_DO_INSERT: + type = NFT_MSG_NEWRULE; + break; + case NFT_DO_REPLACE: + type = NFT_MSG_NEWRULE; + flags |= NLM_F_REPLACE; + break; + case NFT_DO_DELETE: + case NFT_DO_FLUSH: + type = NFT_MSG_DELRULE; + break; + default: + return 0; + } - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) { - if (errno != EEXIST) - perror("mnl-talk:nft_commit"); + nlh = nft_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch), + type, h->family, flags, seq++); + nft_rule_nlmsg_build_payload(nlh, n->rule); + nft_rule_print_debug(n->rule, nlh); + + h->rule_list_num--; + list_del(&n->head); + nft_rule_free(n->rule); + free(n); + + if (!mnl_nlmsg_batch_next(h->batch)) + h->batch = mnl_nft_batch_page_add(h->batch); + } + + switch (action) { + case NFT_DO_COMMIT: + mnl_nft_batch_end(h->batch, seq++); + break; + case NFT_DO_ABORT: + break; } + + if (!mnl_nlmsg_batch_is_empty(h->batch)) + h->batch = mnl_nft_batch_page_add(h->batch); + + ret = mnl_nft_batch_talk(h); + if (ret < 0) + perror("mnl_nft_batch_talk:"); + + mnl_nlmsg_batch_reset(h->batch); + return ret == 0 ? 1 : 0; } int nft_commit(struct nft_handle *h) { - return nft_action(h, NFT_MSG_COMMIT); + return nft_action(h, NFT_DO_COMMIT); } int nft_abort(struct nft_handle *h) { - return nft_action(h, NFT_MSG_ABORT); + return nft_action(h, NFT_DO_ABORT); } int nft_compatible_revision(const char *name, uint8_t rev, int opt) diff --git a/iptables/nft.h b/iptables/nft.h index 50971e86..26b60b99 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -3,6 +3,7 @@ #include "xshared.h" #include "nft-shared.h" +#include #define FILTER 0 #define MANGLE 1 @@ -28,7 +29,9 @@ struct nft_handle { struct mnl_socket *nl; uint32_t portid; uint32_t seq; - bool commit; + struct list_head rule_list; + int rule_list_num; + struct mnl_nlmsg_batch *batch; struct nft_family_ops *ops; struct builtin_table *tables; }; diff --git a/iptables/xtables-arp-standalone.c b/iptables/xtables-arp-standalone.c index 186c9a1e..8d4679f1 100644 --- a/iptables/xtables-arp-standalone.c +++ b/iptables/xtables-arp-standalone.c @@ -81,6 +81,8 @@ int xtables_arp_main(int argc, char *argv[]) #endif ret = do_commandarp(&h, argc, argv, &table); + if (ret) + ret = nft_commit(&h); exit(!ret); } diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 608e189b..06053f62 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -166,7 +166,6 @@ xtables_restore_main(int argc, char *argv[]) { struct nft_handle h = { .family = AF_INET, /* default to IPv4 */ - .commit = true, }; char buffer[10240]; int c; diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index 9d5a6675..cb685d64 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -70,6 +70,9 @@ xtables_main(int argc, char *argv[]) } ret = do_commandx(&h, argc, argv, &table); + if (ret) + ret = nft_commit(&h); + if (!ret) { if (errno == EINVAL) { fprintf(stderr, "iptables: %s. " -- cgit v1.2.3 From 0363995ef12c2377875f9ab60a43b9b601cb2560 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 3 Oct 2013 12:52:55 +0300 Subject: xtables: arp: Store target entry properly and compare them relevantly Fixes a segfault issue when deleting a rule. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 44 +++++++++++++++++++++----------------------- iptables/nft-shared.h | 4 ++-- iptables/xtables-arp.c | 8 ++++---- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index 10c7b63e..04600662 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -276,17 +276,16 @@ static void nft_arp_parse_meta(struct nft_rule_expr *e, uint8_t key, fw->arp.invflags |= ipt_to_arpt_flags(flags); } -static void nft_arp_parse_target(struct xtables_target *t, void *data) +static void nft_arp_parse_target(struct xtables_target *target, void *data) { struct arpt_entry *fw = data; - size_t size = sizeof(struct arpt_entry); - struct xt_entry_target **target; + struct xt_entry_target **t; - fw->target_offset = size; - fw->next_offset = size + t->t->u.target_size; + fw->target_offset = offsetof(struct arpt_entry, elems); + fw->next_offset = fw->target_offset + target->t->u.target_size; - target = (void *) fw + fw->target_offset; - *target = t->t; + t = (void *) &fw->elems; + *t = target->t; } static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto, @@ -297,10 +296,13 @@ static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto, target = xtables_find_target(XT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED); - size = sizeof(struct xt_entry_target) + target->size; + + size = XT_ALIGN(sizeof(struct xt_entry_target)) + target->size; + target->t = xtables_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); + target->t->u.user.revision = target->revision; nft_arp_parse_target(target, data); } @@ -598,16 +600,11 @@ static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nft_rule *r, struct arpt_entry *fw = data; struct xt_entry_target *t_fw, *t_this; char *targname_fw, *targname_this; - struct xtables_target *target_fw, *target_this; struct arpt_entry this = {}; /* Delete by matching rule case */ nft_rule_to_arpt_entry(r, &this); - DEBUGP("comparing with... "); - -/* nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0); */ - if (!ops->is_same(fw, &this)) return false; @@ -617,19 +614,20 @@ static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nft_rule *r, targname_fw = t_fw->u.user.name; targname_this = t_this->u.user.name; - target_fw = xtables_find_target(targname_fw, XTF_TRY_LOAD); - target_this = xtables_find_target(targname_this, XTF_TRY_LOAD); - - if (target_fw != NULL && target_this != NULL) { - if (!compare_targets(target_fw, target_this)) { + if (!strcmp(targname_fw, targname_this) && + (!strcmp(targname_fw, "mangle") || + !strcmp(targname_fw, "CLASSIFY"))) { + if (memcmp(t_fw->data, t_this->data, + t_fw->u.user.target_size - sizeof(*t_fw)) != 0) { DEBUGP("Different target\n"); return false; } - } else { - if (strcmp(targname_fw, targname_this) != 0) { - DEBUGP("Different verdict\n"); - return false; - } + return true; + } + + if (strcmp(targname_fw, targname_this) != 0) { + DEBUGP("Different verdict\n"); + return false; } return true; diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 3d1f433f..7260fddc 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -178,11 +178,11 @@ extern char *opcodes[]; #include -static inline struct xt_entry_target *nft_arp_get_target(struct arpt_entry *fw) +static inline struct xt_entry_target *nft_arp_get_target(struct arpt_entry *fw) { struct xt_entry_target **target; - target = (void *) fw + fw->target_offset; + target = (void *) &fw->elems; return *target; } diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 407fb066..8072d90d 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -819,12 +819,12 @@ generate_entry(const struct arpt_entry *fw, size = sizeof(struct arpt_entry); - e = xtables_malloc(size + target->u.target_size); + e = xtables_malloc(size); *e = *fw; - e->target_offset = size; - e->next_offset = size + target->u.target_size; + e->target_offset = offsetof(struct arpt_entry, elems); + e->next_offset = e->target_offset + target->u.target_size; - t = (void *) e + e->target_offset; + t = (void *) &e->elems; *t = target; return e; -- cgit v1.2.3 From 278add3f0d6d54b8b2e5f0bf6fbe62314e645a79 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 3 Oct 2013 12:52:56 +0300 Subject: extensions: add arptables' libxt_mangle.c for xtables-arp This is a porting of the original mangle target in arptables. This also adapts original code so functions fits with libxtables. This is needed by the xtables-arp compatibility tool for nftables. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- extensions/libxt_mangle.c | 389 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 extensions/libxt_mangle.c diff --git a/extensions/libxt_mangle.c b/extensions/libxt_mangle.c new file mode 100644 index 00000000..822033c8 --- /dev/null +++ b/extensions/libxt_mangle.c @@ -0,0 +1,389 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Authors: + * Libarptc code from: Bart De Schuymer + * Port to libxtables: Tomasz Bursztyka + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static void mangle_help(void) +{ + printf( +"mangle target options:\n" +"--mangle-ip-s IP address\n" +"--mangle-ip-d IP address\n" +"--mangle-mac-s MAC address\n" +"--mangle-mac-d MAC address\n" +"--mangle-target target (DROP, CONTINUE or ACCEPT -- default is ACCEPT)\n" + ); +} + +#define MANGLE_IPS '1' +#define MANGLE_IPT '2' +#define MANGLE_DEVS '3' +#define MANGLE_DEVT '4' +#define MANGLE_TARGET '5' +static const struct xt_option_entry mangle_opts[] = { + { .name = "mangle-ip-s", .id = MANGLE_IPS, .type = XTTYPE_STRING, + .flags = XTOPT_MAND }, + { .name = "mangle-ip-d", .id = MANGLE_IPT, .type = XTTYPE_STRING, + .flags = XTOPT_MAND }, + { .name = "mangle-mac-s", .id = MANGLE_DEVS, .type = XTTYPE_STRING, + .flags = XTOPT_MAND }, + { .name = "mangle-mac-d", .id = MANGLE_DEVT, .type = XTTYPE_STRING, + .flags = XTOPT_MAND }, + { .name = "mangle-target", .id = MANGLE_TARGET, .type = XTTYPE_STRING, + .flags = XTOPT_MAND }, + XTOPT_TABLEEND, +}; + + +static struct in_addr *network_to_addr(const char *name) +{ + struct netent *net; + static struct in_addr addr; + + if ((net = getnetbyname(name)) != NULL) { + if (net->n_addrtype != AF_INET) + return (struct in_addr *) NULL; + addr.s_addr = htonl((unsigned long) net->n_net); + return &addr; + } + + return (struct in_addr *) NULL; +} + +static void inaddrcpy(struct in_addr *dst, struct in_addr *src) +{ + dst->s_addr = src->s_addr; +} + +static struct in_addr *host_to_addr(const char *name, unsigned int *naddr) +{ + struct hostent *host; + struct in_addr *addr; + unsigned int i; + + *naddr = 0; + if ((host = gethostbyname(name)) != NULL) { + if (host->h_addrtype != AF_INET || + host->h_length != sizeof(struct in_addr)) + return (struct in_addr *) NULL; + + while (host->h_addr_list[*naddr] != (char *) NULL) + (*naddr)++; + addr = xtables_calloc(*naddr, sizeof(struct in_addr)); + for (i = 0; i < *naddr; i++) + inaddrcpy(&(addr[i]), + (struct in_addr *) host->h_addr_list[i]); + return addr; + } + + return (struct in_addr *) NULL; +} + +static int string_to_number(const char *s, unsigned int min, + unsigned int max, unsigned int *ret) +{ + long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtol(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && number <= max) { + *ret = number; + return 0; + } + } + return -1; +} + +static struct in_addr *dotted_to_addr(const char *dotted) +{ + static struct in_addr addr; + unsigned char *addrp; + char *p, *q; + unsigned int onebyte; + int i; + char buf[20]; + + /* copy dotted string, because we need to modify it */ + strncpy(buf, dotted, sizeof(buf) - 1); + addrp = (unsigned char *) &(addr.s_addr); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return (struct in_addr *) NULL; + + *q = '\0'; + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[i] = (unsigned char) onebyte; + p = q + 1; + } + + /* we've checked 3 bytes, now we check the last one */ + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[3] = (unsigned char) onebyte; + + return &addr; +} + +static struct in_addr *parse_hostnetwork(const char *name, + unsigned int *naddrs) +{ + struct in_addr *addrp, *addrptmp; + + if ((addrptmp = dotted_to_addr(name)) != NULL || + (addrptmp = network_to_addr(name)) != NULL) { + addrp = xtables_malloc(sizeof(struct in_addr)); + inaddrcpy(addrp, addrptmp); + *naddrs = 1; + return addrp; + } + if ((addrp = host_to_addr(name, naddrs)) != NULL) + return addrp; + + xtables_error(PARAMETER_PROBLEM, "host/network `%s' not found", name); +} + +static void mangle_parse(struct xt_option_call *cb) +{ + const struct arpt_entry *e = cb->xt_entry; + struct arpt_mangle *mangle = cb->data; + struct in_addr *ipaddr; + struct ether_addr *macaddr; + + /* mangle target is by default "ACCEPT". Setting it here, + * since original arpt_mangle.c init() no longer exists*/ + mangle->target = NF_ACCEPT; + + xtables_option_parse(cb); + switch (cb->entry->id) { + case MANGLE_IPS: +/* + if (e->arp.arpln_mask == 0) + xtables_error(PARAMETER_PROBLEM, "no pln defined"); + + if (e->arp.invflags & ARPT_INV_ARPPLN) + xtables_error(PARAMETER_PROBLEM, + "! pln not allowed for --mangle-ip-s"); +*/ +/* + if (e->arp.arpln != 4) + xtables_error(PARAMETER_PROBLEM, "only pln=4 supported"); +*/ + { + unsigned int nr; + ipaddr = parse_hostnetwork(cb->arg, &nr); + } + mangle->u_s.src_ip.s_addr = ipaddr->s_addr; + free(ipaddr); + mangle->flags |= ARPT_MANGLE_SIP; + break; + case MANGLE_IPT: +/* + if (e->arp.arpln_mask == 0) + xtables_error(PARAMETER_PROBLEM, "no pln defined"); + + if (e->arp.invflags & ARPT_INV_ARPPLN) + xtables_error(PARAMETER_PROBLEM, + "! pln not allowed for --mangle-ip-d"); +*/ +/* + if (e->arp.arpln != 4) + xtables_error(PARAMETER_PROBLEM, "only pln=4 supported"); +*/ + { + unsigned int nr; + ipaddr = parse_hostnetwork(cb->arg, &nr); + } + mangle->u_t.tgt_ip.s_addr = ipaddr->s_addr; + free(ipaddr); + mangle->flags |= ARPT_MANGLE_TIP; + break; + case MANGLE_DEVS: + if (e->arp.arhln_mask == 0) + xtables_error(PARAMETER_PROBLEM, + "no --h-length defined"); + if (e->arp.invflags & ARPT_INV_ARPHLN) + xtables_error(PARAMETER_PROBLEM, + "! --h-length not allowed for " + "--mangle-mac-s"); + if (e->arp.arhln != 6) + xtables_error(PARAMETER_PROBLEM, + "only --h-length 6 supported"); + macaddr = ether_aton(cb->arg); + if (macaddr == NULL) + xtables_error(PARAMETER_PROBLEM, "invalid source MAC"); + memcpy(mangle->src_devaddr, macaddr, e->arp.arhln); + mangle->flags |= ARPT_MANGLE_SDEV; + break; + case MANGLE_DEVT: + if (e->arp.arhln_mask == 0) + xtables_error(PARAMETER_PROBLEM, + "no --h-length defined"); + if (e->arp.invflags & ARPT_INV_ARPHLN) + xtables_error(PARAMETER_PROBLEM, + "! hln not allowed for --mangle-mac-d"); + if (e->arp.arhln != 6) + xtables_error(PARAMETER_PROBLEM, + "only --h-length 6 supported"); + macaddr = ether_aton(cb->arg); + if (macaddr == NULL) + xtables_error(PARAMETER_PROBLEM, "invalid target MAC"); + memcpy(mangle->tgt_devaddr, macaddr, e->arp.arhln); + mangle->flags |= ARPT_MANGLE_TDEV; + break; + case MANGLE_TARGET: + if (!strcmp(cb->arg, "DROP")) + mangle->target = NF_DROP; + else if (!strcmp(cb->arg, "ACCEPT")) + mangle->target = NF_ACCEPT; + else if (!strcmp(cb->arg, "CONTINUE")) + mangle->target = ARPT_CONTINUE; + else + xtables_error(PARAMETER_PROBLEM, + "bad target for --mangle-target"); + break; + } +} + +static void mangle_fcheck(struct xt_fcheck_call *cb) +{ +} + +static char *addr_to_dotted(const struct in_addr *addrp) +{ + static char buf[20]; + const unsigned char *bytep; + + bytep = (const unsigned char *) &(addrp->s_addr); + sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); + return buf; +} + +static char *addr_to_host(const struct in_addr *addr) +{ + struct hostent *host; + + if ((host = gethostbyaddr((char *) addr, + sizeof(struct in_addr), AF_INET)) != NULL) + return (char *) host->h_name; + + return (char *) NULL; +} + +static char *addr_to_network(const struct in_addr *addr) +{ + struct netent *net; + + if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) + return (char *) net->n_name; + + return (char *) NULL; +} + +static char *addr_to_anyname(const struct in_addr *addr) +{ + char *name; + + if ((name = addr_to_host(addr)) != NULL || + (name = addr_to_network(addr)) != NULL) + return name; + + return addr_to_dotted(addr); +} + +static void print_mac(const unsigned char *mac, int l) +{ + int j; + + for (j = 0; j < l; j++) + printf("%02x%s", mac[j], + (j==l-1) ? "" : ":"); +} + +static void mangle_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct arpt_mangle *m = (const void *)target; + char buf[100]; + + if (m->flags & ARPT_MANGLE_SIP) { + if (numeric) + sprintf(buf, "%s", addr_to_dotted(&(m->u_s.src_ip))); + else + sprintf(buf, "%s", addr_to_anyname(&(m->u_s.src_ip))); + printf("--mangle-ip-s %s ", buf); + } + if (m->flags & ARPT_MANGLE_SDEV) { + printf("--mangle-mac-s "); + print_mac((unsigned char *)m->src_devaddr, 6); + printf(" "); + } + if (m->flags & ARPT_MANGLE_TIP) { + if (numeric) + sprintf(buf, "%s", addr_to_dotted(&(m->u_t.tgt_ip))); + else + sprintf(buf, "%s", addr_to_anyname(&(m->u_t.tgt_ip))); + printf("--mangle-ip-d %s ", buf); + } + if (m->flags & ARPT_MANGLE_TDEV) { + printf("--mangle-mac-d "); + print_mac((unsigned char *)m->tgt_devaddr, 6); + printf(" "); + } + if (m->target != NF_ACCEPT) { + printf("--mangle-target "); + if (m->target == NF_DROP) + printf("DROP "); + else + printf("CONTINUE "); + } +} + +static void mangle_save(const void *ip, const struct xt_entry_target *target) +{ +} + +static struct xtables_target mangle_tg_reg = { + .family = NFPROTO_ARP, + .name = "mangle", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct arpt_mangle)), + .userspacesize = XT_ALIGN(sizeof(struct arpt_mangle)), + .help = mangle_help, + .x6_parse = mangle_parse, + .x6_fcheck = mangle_fcheck, + .print = mangle_print, + .save = mangle_save, + .x6_options = mangle_opts, +}; + +void _init(void) +{ + xtables_register_target(&mangle_tg_reg); +} -- cgit v1.2.3 From d0d6b2833fa9d79aa1c8818a287c594f0eddfb6c Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 3 Oct 2013 14:28:05 +0300 Subject: extensions: libxt_mangle: Fixes option issues Fix option IDs and remove irrelevant flags on setting options mandatory. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- extensions/libxt_mangle.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/extensions/libxt_mangle.c b/extensions/libxt_mangle.c index 822033c8..4b20feb3 100644 --- a/extensions/libxt_mangle.c +++ b/extensions/libxt_mangle.c @@ -33,22 +33,21 @@ static void mangle_help(void) ); } -#define MANGLE_IPS '1' -#define MANGLE_IPT '2' -#define MANGLE_DEVS '3' -#define MANGLE_DEVT '4' -#define MANGLE_TARGET '5' +enum { + MANGLE_IPS = 0, + MANGLE_IPT = 1, + MANGLE_DEVS = 2, + MANGLE_DEVT = 3, + MANGLE_TARGET = 4, +}; + static const struct xt_option_entry mangle_opts[] = { - { .name = "mangle-ip-s", .id = MANGLE_IPS, .type = XTTYPE_STRING, - .flags = XTOPT_MAND }, - { .name = "mangle-ip-d", .id = MANGLE_IPT, .type = XTTYPE_STRING, - .flags = XTOPT_MAND }, - { .name = "mangle-mac-s", .id = MANGLE_DEVS, .type = XTTYPE_STRING, - .flags = XTOPT_MAND }, - { .name = "mangle-mac-d", .id = MANGLE_DEVT, .type = XTTYPE_STRING, - .flags = XTOPT_MAND }, - { .name = "mangle-target", .id = MANGLE_TARGET, .type = XTTYPE_STRING, - .flags = XTOPT_MAND }, + { .name = "mangle-ip-s", .id = MANGLE_IPS, .type = XTTYPE_STRING }, + { .name = "mangle-ip-d", .id = MANGLE_IPT, .type = XTTYPE_STRING }, + { .name = "mangle-mac-s", .id = MANGLE_DEVS, .type = XTTYPE_STRING }, + { .name = "mangle-mac-d", .id = MANGLE_DEVT, .type = XTTYPE_STRING }, + { .name = "mangle-target", .id = MANGLE_TARGET, + .type = XTTYPE_STRING }, XTOPT_TABLEEND, }; -- cgit v1.2.3 From ea23cfc0e663a934b05e5c09cbed5cda3c999f6f Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 3 Oct 2013 14:39:22 +0300 Subject: nft: Header inclusion missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes compilation issues. nft-shared.c: In function ‘nft_ipv46_rule_find’: nft-shared.c:725:2: warning: implicit declaration of function ‘nft_rule_print_save’ [-Wimplicit-function-declaration] nft-shared.c:725:32: error: ‘NFT_RULE_APPEND’ undeclared (first use in this function) nft-shared.c:725:32: note: each undeclared identifier is reported only once for each function it appears in Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 5c09fe1d..c9bde90b 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -27,6 +27,7 @@ #include "nft-shared.h" #include "xshared.h" +#include "nft.h" extern struct nft_family_ops nft_family_ops_ipv4; extern struct nft_family_ops nft_family_ops_ipv6; -- cgit v1.2.3 From f2f3a4e6d5f2e64769f9e7946f594a3d07f48cda Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 3 Oct 2013 16:00:47 +0300 Subject: xtables: arp: Parse properly target options Target never gets its options parsed. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-arp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 8072d90d..411a6998 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -1317,6 +1317,10 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) exit_tryhelp(2); default: + if (target) { + xtables_option_tpcall(c, argv, + invert, target, &fw); + } break; } invert = FALSE; @@ -1325,8 +1329,6 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) if (target) xtables_option_tfcall(target); - /* Fix me: must put inverse options checking here --MN */ - if (optind < argc) xtables_error(PARAMETER_PROBLEM, "unknown arguments found on commandline"); -- cgit v1.2.3 From e8a218f27a3d7948697c1c1d8f364af6f65b5ac9 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 3 Oct 2013 16:00:59 +0300 Subject: nft: fix wrong target size The allocated area was not aligned. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index c9bde90b..25cb1772 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -292,18 +292,21 @@ void nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, struct xtables_target *target; struct xt_entry_target *t; struct nft_family_ops *ops; + size_t size; target = xtables_find_target(targname, XTF_TRY_LOAD); if (target == NULL) return; - t = calloc(1, sizeof(struct xt_entry_target) + tg_len); + size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len; + + t = calloc(1, size); if (t == NULL) { fprintf(stderr, "OOM"); exit(EXIT_FAILURE); } memcpy(&t->data, targinfo, tg_len); - t->u.target_size = tg_len + XT_ALIGN(sizeof(struct xt_entry_target)); + t->u.target_size = size; t->u.user.revision = nft_rule_expr_get_u32(e, NFT_EXPR_TG_REV); strcpy(t->u.user.name, target->name); -- cgit v1.2.3 From e55b3e367129cee58a3b2754d6882efeeccd1b9f Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Thu, 3 Oct 2013 16:31:39 +0200 Subject: nft: arp: initialize flags in nft_arp_parse_meta This small patch initialize the flags in nft_arp_parse_meta, now the nft_arp_rule_find is working properly. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index 04600662..570962d3 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -267,7 +267,7 @@ static void nft_arp_parse_meta(struct nft_rule_expr *e, uint8_t key, void *data) { struct arpt_entry *fw = data; - uint8_t flags; + uint8_t flags = 0; parse_meta(e, key, fw->arp.iniface, fw->arp.iniface_mask, fw->arp.outiface, fw->arp.outiface_mask, -- cgit v1.2.3 From e8cbd65dcef62333b5e461cb264c844065b33e9a Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Sun, 6 Oct 2013 14:36:47 +0200 Subject: nft: arp: add parse_target to nft_family_ops_arp This patch adds parse_target for arp family, fixing segfaults for xtables-arp -L after rules like: xtables-arp -A OUTPUT -o eth0 -j CLASSIFY --set-class 0:7 xtables-arp -A OUTPUT -o eth1 --h-length 6 \ -j mangle --mangle-mac-s 01:00:5e:00:01:01 Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index 570962d3..08f88145 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -643,4 +643,5 @@ struct nft_family_ops nft_family_ops_arp = { .print_firewall = nft_arp_print_firewall, .post_parse = NULL, .rule_find = nft_arp_rule_find, + .parse_target = nft_arp_parse_target, }; -- cgit v1.2.3 From 4b7a4afaa240e5d2039e612e125b045d5d1cb7fa Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 8 Oct 2013 10:50:39 +0200 Subject: xtables: fix missing ipt_entry for MASQUERADE target The MASQUERADE target relies on the ipt_entry information that is set in ->post_parse, which is too late. Add a new hook called ->pre_parse, that sets the protocol information accordingly. Thus: xtables -4 -A POSTROUTING -t nat -p tcp \ -j MASQUERADE --to-ports 1024 works again. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 10 ++++++++-- iptables/nft-ipv6.c | 18 ++++++++++++------ iptables/nft-shared.h | 2 ++ iptables/xtables.c | 19 +++++++++++++++---- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 3be801d3..2ac823fc 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -331,12 +331,17 @@ static uint8_t nft_ipv4_save_firewall(const struct iptables_command_state *cs, return cs->fw.ip.flags; } +static void nft_ipv4_proto_parse(struct iptables_command_state *cs, + struct xtables_args *args) +{ + cs->fw.ip.proto = args->proto; + cs->fw.ip.invflags = args->invflags; +} + static void nft_ipv4_post_parse(int command, struct iptables_command_state *cs, struct xtables_args *args) { - cs->fw.ip.proto = args->proto; - cs->fw.ip.invflags = args->invflags; cs->fw.ip.flags = args->flags; strncpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ); @@ -400,6 +405,7 @@ struct nft_family_ops nft_family_ops_ipv4 = { .parse_immediate = nft_ipv4_parse_immediate, .print_firewall = nft_ipv4_print_firewall, .save_firewall = nft_ipv4_save_firewall, + .proto_parse = nft_ipv4_proto_parse, .post_parse = nft_ipv4_post_parse, .parse_target = nft_ipv4_parse_target, .rule_find = nft_ipv4_rule_find, diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index e3784a8b..b02d9526 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -243,15 +243,11 @@ static int is_exthdr(uint16_t proto) proto == IPPROTO_DSTOPTS); } -static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs, - struct xtables_args *args) +static void nft_ipv6_proto_parse(struct iptables_command_state *cs, + struct xtables_args *args) { - if (args->proto != 0) - args->flags |= IP6T_F_PROTO; - cs->fw6.ipv6.proto = args->proto; cs->fw6.ipv6.invflags = args->invflags; - cs->fw6.ipv6.flags = args->flags; if (is_exthdr(cs->fw6.ipv6.proto) && (cs->fw6.ipv6.invflags & XT_INV_PROTO) == 0) @@ -259,6 +255,15 @@ static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs, "Warning: never matched protocol: %s. " "use extension match instead.\n", cs->protocol); +} + +static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs, + struct xtables_args *args) +{ + if (args->proto != 0) + args->flags |= IP6T_F_PROTO; + + cs->fw6.ipv6.flags = args->flags; strncpy(cs->fw6.ipv6.iniface, args->iniface, IFNAMSIZ); memcpy(cs->fw6.ipv6.iniface_mask, @@ -323,6 +328,7 @@ struct nft_family_ops nft_family_ops_ipv6 = { .parse_immediate = nft_ipv6_parse_immediate, .print_firewall = nft_ipv6_print_firewall, .save_firewall = nft_ipv6_save_firewall, + .proto_parse = nft_ipv6_proto_parse, .post_parse = nft_ipv6_post_parse, .parse_target = nft_ipv6_parse_target, .rule_find = nft_ipv6_rule_find, diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 7260fddc..9df17bc3 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -51,6 +51,8 @@ struct nft_family_ops { unsigned int format); uint8_t (*save_firewall)(const struct iptables_command_state *cs, unsigned int format); + void (*proto_parse)(struct iptables_command_state *cs, + struct xtables_args *args); void (*post_parse)(int command, struct iptables_command_state *cs, struct xtables_args *args); void (*parse_target)(struct xtables_target *t, void *data); diff --git a/iptables/xtables.c b/iptables/xtables.c index 279b77bf..c49b4a2f 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -715,6 +715,11 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) demand-load a protocol. */ opterr = 0; + /* Default on AF_INET */ + h->ops = nft_family_ops_lookup(AF_INET); + if (h->ops == NULL) + xtables_error(PARAMETER_PROBLEM, "Unknown family"); + opts = xt_params->orig_opts; while ((cs.c = getopt_long(argc, argv, "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:46", @@ -894,6 +899,9 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) if (args.proto == 0 && (args.invflags & XT_INV_PROTO)) xtables_error(PARAMETER_PROBLEM, "rule would never match protocol"); + + /* This needs to happen here to parse extensions */ + h->ops->proto_parse(&cs, &args); break; case 's': @@ -1033,11 +1041,18 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) case '4': if (args.family != AF_INET) exit_tryhelp(2); + + h->ops = nft_family_ops_lookup(args.family); break; case '6': args.family = AF_INET6; xtables_set_nfproto(AF_INET6); + + h->ops = nft_family_ops_lookup(args.family); + if (h->ops == NULL) + xtables_error(PARAMETER_PROBLEM, + "Unknown family"); break; case 1: /* non option */ @@ -1089,10 +1104,6 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) if (h->family == AF_UNSPEC) h->family = args.family; - h->ops = nft_family_ops_lookup(h->family); - if (h->ops == NULL) - xtables_error(PARAMETER_PROBLEM, "Unknown family"); - h->ops->post_parse(command, &cs, &args); if (command == CMD_REPLACE && -- cgit v1.2.3 From 5f6e384ac2a3d7b647a909654a3bdee1c0bcb3eb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 8 Oct 2013 10:44:20 +0200 Subject: nft: pass ipt_entry to ->save_firewall hook The extension needs the ipt_entry not to crash. Since cs->fw actually points to an union that also contains cs->fw6, just pass cs->fw to make it work. This fixes: -A INPUT -p tcp -m multiport --ports 1,2,3,4,6,7,8,9,10,11,12,13,14,15 Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 4 ++-- iptables/nft.c | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 25cb1772..ebcb9692 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -633,14 +633,14 @@ void print_matches_and_target(struct iptables_command_state *cs, for (matchp = cs->matches; matchp; matchp = matchp->next) { if (matchp->match->print != NULL) { - matchp->match->print(NULL, matchp->match->m, + matchp->match->print(&cs->fw, matchp->match->m, format & FMT_NUMERIC); } } if (cs->target != NULL) { if (cs->target->print != NULL) { - cs->target->print(NULL, cs->target->t, + cs->target->print(&cs->fw, cs->target->t, format & FMT_NUMERIC); } } diff --git a/iptables/nft.c b/iptables/nft.c index 50e756be..01e02640 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1040,8 +1040,10 @@ nft_rule_print_save(const struct iptables_command_state *cs, } else printf("-m %s", matchp->match->name); - if (matchp->match->save != NULL) - matchp->match->save(NULL, matchp->match->m); + if (matchp->match->save != NULL) { + /* cs->fw union makes the trick */ + matchp->match->save(&cs->fw, matchp->match->m); + } printf(" "); } @@ -1052,7 +1054,7 @@ nft_rule_print_save(const struct iptables_command_state *cs, printf("-j %s", cs->jumpto); if (cs->target->save != NULL) - cs->target->save(NULL, cs->target->t); + cs->target->save(&cs->fw, cs->target->t); } else if (strlen(cs->jumpto) > 0) printf("-%c %s", ip_flags & IPT_F_GOTO ? 'g' : 'j', cs->jumpto); -- cgit v1.2.3 From 6cd426bc7593ecf04a02c901d94e04093bdf69e4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 8 Oct 2013 12:13:57 +0200 Subject: nft: fix bad length when comparing extension data area Use ->userspacesize to compare the extension data area, otherwise we also compare the internal private pointers which are only meaningful to the kernelspace. This fixes: xtables -4 -D INPUT -m connlimit \ --connlimit-above 10 --connlimit-mask 32 --connlimit-daddr But it also fixes many other matches/targets which use internal private data. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index ebcb9692..3987f74b 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -683,7 +683,7 @@ compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2) } if (memcmp(m1->data, m2->data, - m1->u.user.match_size - sizeof(*m1)) != 0) { + mp1->match->userspacesize) != 0) { DEBUGP("mismatch match data\n"); return false; } @@ -709,10 +709,8 @@ bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2) if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0) return false; - if (memcmp(tg1->t->data, tg2->t->data, - tg1->t->u.user.target_size - sizeof(*tg1->t)) != 0) { + if (memcmp(tg1->t->data, tg2->t->data, tg1->userspacesize) != 0) return false; - } return true; } -- cgit v1.2.3 From 28dcf16384b223f9890567bd89056864a7e3c85d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 11 Oct 2013 11:42:36 +0200 Subject: nft: fix interface wildcard matching In (73ea1cc nft: convert rule into a command state structure), the interface wildcard matching got broken. The previous handling was flawed by the use of ifnametoindex in scenario where the interface may vanished after a rule was added. This approach relies on the trailing '\0' to identify if this is an exact or wildcard matching, based on discussion with Florian. Based on initial patch from Anand Raj Manickam. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-shared.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 3987f74b..e0eaa170 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -124,13 +124,11 @@ void add_iniface(struct nft_rule *r, char *iface, int invflags) else op = NFT_CMP_EQ; - if (iface[iface_len - 1] == '+') { - add_meta(r, NFT_META_IIFNAME); + add_meta(r, NFT_META_IIFNAME); + if (iface[iface_len - 1] == '+') add_cmp_ptr(r, op, iface, iface_len - 1); - } else { - add_meta(r, NFT_META_IIF); - add_cmp_u32(r, if_nametoindex(iface), op); - } + else + add_cmp_ptr(r, op, iface, iface_len + 1); } void add_outiface(struct nft_rule *r, char *iface, int invflags) @@ -145,13 +143,11 @@ void add_outiface(struct nft_rule *r, char *iface, int invflags) else op = NFT_CMP_EQ; - if (iface[iface_len - 1] == '+') { - add_meta(r, NFT_META_OIFNAME); + add_meta(r, NFT_META_OIFNAME); + if (iface[iface_len - 1] == '+') add_cmp_ptr(r, op, iface, iface_len - 1); - } else { - add_meta(r, NFT_META_OIF); - add_cmp_u32(r, if_nametoindex(iface), op); - } + else + add_cmp_ptr(r, op, iface, iface_len + 1); } void add_addr(struct nft_rule *r, int offset, @@ -251,15 +247,14 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, *invflags |= IPT_INV_VIA_IN; memcpy(iniface, ifname, len); - iniface[len] = '\0'; - /* If zero, then this is an interface mask */ - if (if_nametoindex(iniface) == 0) { + if (iniface[len] == '\0') + memset(iniface_mask, 0xff, len); + else { iniface[len] = '+'; iniface[len+1] = '\0'; + memset(iniface_mask, 0xff, len + 1); } - - memset(iniface_mask, 0xff, len); break; case NFT_META_OIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); @@ -267,15 +262,14 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, *invflags |= IPT_INV_VIA_OUT; memcpy(outiface, ifname, len); - outiface[len] = '\0'; - /* If zero, then this is an interface mask */ - if (if_nametoindex(outiface) == 0) { + if (outiface[len] == '\0') + memset(outiface_mask, 0xff, len); + else { outiface[len] = '+'; outiface[len+1] = '\0'; + memset(outiface_mask, 0xff, len + 1); } - - memset(outiface_mask, 0xff, len); break; default: DEBUGP("unknown meta key %d\n", key); -- cgit v1.2.3 From 0cfd537e8020812ef02ce0e27b8b22a94d3820c3 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Sat, 12 Oct 2013 11:21:52 +0200 Subject: nft: arp: fix possible string overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch replaces strcat with strncat and strcpy with strncpy fixing possible string overflow. Based on the original patch: http://patchwork.ozlabs.org/patch/279672/ from Jaromír Končický via Jiri Popelka. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 6 ++++-- iptables/xtables-arp.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index 08f88145..17101369 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -459,7 +459,8 @@ nft_arp_print_firewall(struct nft_rule *r, unsigned int num, sprintf(buf, "%s", addr_to_dotted(&(fw.arp.src))); else sprintf(buf, "%s", addr_to_anyname(&(fw.arp.src))); - strcat(buf, mask_to_dotted(&(fw.arp.smsk))); + strncat(buf, mask_to_dotted(&(fw.arp.smsk)), + sizeof(buf) - strlen(buf) - 1); printf("-s %s ", buf); } @@ -483,7 +484,8 @@ after_devsrc: sprintf(buf, "%s", addr_to_dotted(&(fw.arp.tgt))); else sprintf(buf, "%s", addr_to_anyname(&(fw.arp.tgt))); - strcat(buf, mask_to_dotted(&(fw.arp.tmsk))); + strncat(buf, mask_to_dotted(&(fw.arp.tmsk)), + sizeof(buf) - strlen(buf) - 1); printf("-d %s ", buf); } diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 411a6998..18f285c6 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -847,7 +847,8 @@ static struct xtables_target *command_jump(struct arpt_entry *fw, target->t = xtables_calloc(1, size); target->t->u.target_size = size; - strcpy(target->t->u.user.name, jumpto); + strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name)); + target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0'; target->t->u.user.revision = target->revision; xs_init_target(target); -- cgit v1.2.3 From 82ab5ac1145dfaae17f86360e6c2db58e6cc59cd Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Wed, 6 Nov 2013 13:51:18 +0200 Subject: xtables: arp: Fix a compilation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes such warning: xtables-arp.c: In function ‘check_inverse’: xtables-arp.c:561:54: attention : declaration of ‘optind’ shadows a global declaration [-Wshadow] Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-arp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 18f285c6..046ae41d 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -557,16 +557,16 @@ add_command(unsigned int *cmd, const int newcmd, const unsigned int othercmds, i } static int -check_inverse(const char option[], int *invert, int *optind, int argc) +check_inverse(const char option[], int *invert, int *optidx, int argc) { if (option && strcmp(option, "!") == 0) { if (*invert) xtables_error(PARAMETER_PROBLEM, "Multiple `!' flags not allowed"); *invert = TRUE; - if (optind) { - *optind = *optind+1; - if (argc && *optind > argc) + if (optidx) { + *optidx = *optidx+1; + if (argc && *optidx > argc) xtables_error(PARAMETER_PROBLEM, "no argument following `!'"); } -- cgit v1.2.3 From b31304a8d88e5d3b4235ac693f56f8a9ca238c32 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Thu, 7 Nov 2013 10:14:37 +0200 Subject: xtables: arp: inhibit -l option so only a fixed 6 bytes length arhln can be used This is a temporary workaround mechanism until variable interface hardware address length can be handled through nftables. This defaults on the length of EUI-64 mac address, which should be the most common usage until this is appropriately fixed for all type of layer 2 addresses. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-arp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 046ae41d..298801b3 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -1145,6 +1145,13 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table) invert); getlength_and_mask(argv[optind - 1], &fw.arp.arhln, &fw.arp.arhln_mask); + + if (fw.arp.arhln != 6) { + xtables_error(PARAMETER_PROBLEM, + "Only harware address length of" + " 6 is supported currently."); + } + break; case 8:/* protocol length */ -- cgit v1.2.3 From 009bb1ae47f76fc538e0d6365448687f27d0f015 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 14 Nov 2013 00:48:07 +0100 Subject: xtables-events: fix compilation due change in libnftables The patch (989b793 src: unify parse and output types) changed the table and chain type definitions, adapt this code to use the new ones. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iptables/xtables-events.c b/iptables/xtables-events.c index 20392a5b..408e091f 100644 --- a/iptables/xtables-events.c +++ b/iptables/xtables-events.c @@ -44,7 +44,7 @@ static int table_cb(const struct nlmsghdr *nlh, int type) goto err_free; } - nft_table_snprintf(buf, sizeof(buf), t, NFT_TABLE_O_DEFAULT, 0); + nft_table_snprintf(buf, sizeof(buf), t, NFT_OUTPUT_DEFAULT, 0); /* FIXME: define syntax to represent table events */ printf("# [table: %s]\t%s\n", type == NFT_MSG_NEWTABLE ? "NEW" : "DEL", buf); @@ -112,7 +112,7 @@ static int chain_cb(const struct nlmsghdr *nlh, int type) goto err_free; } - nft_chain_snprintf(buf, sizeof(buf), t, NFT_CHAIN_O_DEFAULT, 0); + nft_chain_snprintf(buf, sizeof(buf), t, NFT_OUTPUT_DEFAULT, 0); /* FIXME: define syntax to represent chain events */ printf("# [chain: %s]\t%s\n", type == NFT_MSG_NEWCHAIN ? "NEW" : "DEL", buf); -- cgit v1.2.3 From ee595bc702ca80f72c522406f6a06ba7c31eb8df Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 18 Nov 2013 13:50:21 +0100 Subject: nft: fix inversion of built-in selectors (0ab045f xtables: fix missing ipt_entry for MASQUERADE target) broke inversion of built-in selectors, such as -s, -d, etc. We need to refresh the invflags if -p is used or set it for first time if -p is not used, otherwise inversion is ignored. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 4 ++++ iptables/nft-ipv6.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 2ac823fc..1afe8b66 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -343,6 +343,10 @@ static void nft_ipv4_post_parse(int command, struct xtables_args *args) { cs->fw.ip.flags = args->flags; + /* We already set invflags in proto_parse, but we need to refresh it + * to include new parsed options. + */ + cs->fw.ip.invflags = args->invflags; strncpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ); memcpy(cs->fw.ip.iniface_mask, diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index b02d9526..f30cec61 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -264,6 +264,10 @@ static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs, args->flags |= IP6T_F_PROTO; cs->fw6.ipv6.flags = args->flags; + /* We already set invflags in proto_parse, but we need to refresh it + * to include new parsed options. + */ + cs->fw6.ipv6.invflags = args->invflags; strncpy(cs->fw6.ipv6.iniface, args->iniface, IFNAMSIZ); memcpy(cs->fw6.ipv6.iniface_mask, -- cgit v1.2.3 From 5eed9118f2620ac07edd553599e2415f00d6f8f3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 26 Nov 2013 13:09:13 +0100 Subject: nft: fix out of bound memory copy Valgrind reports an invalid read after a memory block: ==11114== Invalid read of size 8 ==11114== at 0x4C2DB02: memcpy@@GLIBC_2.14 (mc_replace_strmem.c:877) ==11114== by 0x41788E: add_match (nft.c:781) ==11114== by 0x41B54C: nft_ipv4_add (nft-ipv4.c:72) ==11114== by 0x415DF2: nft_rule_new.isra.2 (nft.c:945) ==11114== by 0x418ACE: nft_rule_append (nft.c:1000) ==11114== by 0x413A92: add_entry.isra.6 (xtables.c:424) ==11114== by 0x4152DE: do_commandx (xtables.c:1184) ==11114== by 0x4134E8: xtables_main (xtables-standalone.c:72) ==11114== by 0x5B87994: (below main) (libc-start.c:260) ==11114== Address 0x61399e8 is 8 bytes after a block of size 48 alloc'd ==11114== at 0x4C2B514: calloc (vg_replace_malloc.c:593) ==11114== by 0x52448C8: xtables_calloc (xtables.c:272) ==11114== by 0x410AC2: command_default (xshared.c:150) ==11114== by 0x4149A2: do_commandx (xtables.c:1075) ==11114== by 0x4134E8: xtables_main (xtables-standalone.c:72) ==11114== by 0x5B87994: (below main) (libc-start.c:260) m->u.match_size also contains the size of the xt_entry_match structure. Fix also the target path which is very similar. Reported-by: Ana Rey Botello Tested-by: Ana Rey Botello Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 01e02640..2135b04c 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -776,7 +776,7 @@ static int __add_match(struct nft_rule_expr *e, struct xt_entry_match *m) if (info == NULL) return -ENOMEM; - memcpy(info, m->data, m->u.match_size); + memcpy(info, m->data, m->u.match_size - sizeof(*m)); nft_rule_expr_set(e, NFT_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m)); return 0; @@ -799,20 +799,17 @@ int add_match(struct nft_rule *r, struct xt_entry_match *m) static int __add_target(struct nft_rule_expr *e, struct xt_entry_target *t) { - void *info = NULL; + void *info; nft_rule_expr_set(e, NFT_EXPR_TG_NAME, t->u.user.name, strlen(t->u.user.name)); nft_rule_expr_set_u32(e, NFT_EXPR_TG_REV, t->u.user.revision); - if (info == NULL) { - info = calloc(1, t->u.target_size); - if (info == NULL) - return -ENOMEM; - - memcpy(info, t->data, t->u.target_size); - } + info = calloc(1, t->u.target_size); + if (info == NULL) + return -ENOMEM; + memcpy(info, t->data, t->u.target_size - sizeof(*t)); nft_rule_expr_set(e, NFT_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t)); return 0; -- cgit v1.2.3 From 959815ef0f99d7b176d2352973bf57792b4ea6f5 Mon Sep 17 00:00:00 2001 From: Ana Rey Date: Mon, 2 Dec 2013 11:43:25 +0100 Subject: xtables-standalone: call nft_fini in the error path This error is shown with valgrind tools: valgrind --leak-check=full xtables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT ==7377== 16 bytes in 1 blocks are still reachable in loss record 1 of 14 ==7377== at 0x4C2B514: calloc (vg_replace_malloc.c:593) ==7377== by 0x574CC76: mnl_socket_open (socket.c:117) ==7377== by 0x417495: nft_init (nft.c:598) ==7377== by 0x4134C2: xtables_main (xtables-standalone.c:64) ==7377== by 0x5B87994: (below main) (libc-start.c:260) This patch calls nft_fini to release the objects that have been allocated in nft_init. This function was not used so far. Signed-off-by: Ana Rey Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-standalone.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index cb685d64..c9f8e15a 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -66,6 +66,7 @@ xtables_main(int argc, char *argv[]) xtables_globals.program_name, xtables_globals.program_version, strerror(errno)); + nft_fini(&h); exit(EXIT_FAILURE); } @@ -73,6 +74,8 @@ xtables_main(int argc, char *argv[]) if (ret) ret = nft_commit(&h); + nft_fini(&h); + if (!ret) { if (errno == EINVAL) { fprintf(stderr, "iptables: %s. " -- cgit v1.2.3 From 6a4033b70dfdcc2df66f4ea51c901786a2b6131c Mon Sep 17 00:00:00 2001 From: Ana Rey Date: Mon, 2 Dec 2013 11:44:48 +0100 Subject: nft: fix memory leaks in nft_xtables_config_load Those errors are shown with the valgrind tool: valgrind --leak-check=full xtables -A INPUT -i eth0 -p tcp --dport 80 ==7377== ==7377== 16 bytes in 1 blocks are definitely lost in loss record 2 of 14 ==7377== at 0x4C2B514: calloc (vg_replace_malloc.c:593) ==7377== by 0x5955B02: nft_table_list_alloc (table.c:425) ==7377== by 0x4186EB: nft_xtables_config_load (nft.c:2427) ==7377== by 0x4189E6: nft_rule_append (nft.c:991) ==7377== by 0x413A7D: add_entry.isra.6 (xtables.c:424) ==7377== by 0x41524A: do_commandx (xtables.c:1176) ==7377== by 0x4134DC: xtables_main (xtables-standalone.c:72) ==7377== by 0x5B87994: (below main) (libc-start.c:260) ==7377== ==7377== 16 bytes in 1 blocks are definitely lost in loss record 3 of 14 ==7377== at 0x4C2B514: calloc (vg_replace_malloc.c:593) ==7377== by 0x5956A32: nft_chain_list_alloc (chain.c:888) ==7377== by 0x4186F3: nft_xtables_config_load (nft.c:2428) ==7377== by 0x4189E6: nft_rule_append (nft.c:991) ==7377== by 0x413A7D: add_entry.isra.6 (xtables.c:424) ==7377== by 0x41524A: do_commandx (xtables.c:1176) ==7377== by 0x4134DC: xtables_main (xtables-standalone.c:72) ==7377== by 0x5B87994: (below main) (libc-start.c:260) Fix these leaks and consolidate error handling in the exit path of nft_xtables_config_load Signed-off-by: Ana Rey Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 2135b04c..0599beb8 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2423,8 +2423,8 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, { struct nft_table_list *table_list = nft_table_list_alloc(); struct nft_chain_list *chain_list = nft_chain_list_alloc(); - struct nft_table_list_iter *titer; - struct nft_chain_list_iter *citer; + struct nft_table_list_iter *titer = NULL; + struct nft_chain_list_iter *citer = NULL; struct nft_table *table; struct nft_chain *chain; uint32_t table_family, chain_family; @@ -2440,7 +2440,7 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, "Fatal error parsing config file: %s\n", strerror(errno)); } - return -1; + goto err; } /* Stage 1) create tables */ @@ -2463,9 +2463,7 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, "table `%s' cannot be create, reason `%s'. Exitting\n", (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME), strerror(errno)); - nft_table_list_iter_destroy(titer); - nft_table_list_free(table_list); - return -1; + goto err; } continue; } @@ -2476,7 +2474,7 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, nft_table_list_free(table_list); if (!found) - return -1; + goto err; /* Stage 2) create chains */ citer = nft_chain_list_iter_create(chain_list); @@ -2497,9 +2495,7 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, "chain `%s' cannot be create, reason `%s'. Exitting\n", (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME), strerror(errno)); - nft_chain_list_iter_destroy(citer); - nft_chain_list_free(chain_list); - return -1; + goto err; } continue; } @@ -2513,6 +2509,17 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, nft_chain_list_free(chain_list); return 0; + +err: + nft_table_list_free(table_list); + nft_chain_list_free(chain_list); + + if (titer != NULL) + nft_table_list_iter_destroy(titer); + if (citer != NULL) + nft_table_list_iter_destroy(citer); + + return -1; } int nft_chain_zero_counters(struct nft_handle *h, const char *chain, -- cgit v1.2.3 From b0194cea194b510c675ca05415da15cff57afe47 Mon Sep 17 00:00:00 2001 From: Ana Rey Date: Mon, 2 Dec 2013 11:45:14 +0100 Subject: iptables: nft: fix memory leaks in nft_fini Those errors are shown with valgrind tool: valgrind --leak-check=full xtables -A INPUT -i eth0 -p tcp --dport 80 ==12554== 40 bytes in 1 blocks are still reachable in loss record 1 of 10 ==12554== at 0x4C2935B: malloc (vg_replace_malloc.c:270) ==12554== by 0x574D755: mnl_nlmsg_batch_start (nlmsg.c:447) ==12554== by 0x416520: nft_action (nft.c:2281) ==12554== by 0x41355E: xtables_main (xtables-standalone.c:75) ==12554== by 0x5B87994: (below main) (libc-start.c:260) ==12554== 135,168 bytes in 1 blocks are still reachable in loss record 9 of 10 ==12554== at 0x4C2935B: malloc (vg_replace_malloc.c:270) ==12554== by 0x415A24: mnl_nft_batch_alloc (nft.c:102) ==12554== by 0x416520: nft_action (nft.c:2281) ==12554== by 0x41355E: xtables_main (xtables-standalone.c:75) ==12554== by 0x5B87994: (below main) (libc-start.c:260) These objects are allocated from nft_init but they were not released appropriately in the exit path. Signed-off-by: Ana Rey Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index 0599beb8..a015947f 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -618,6 +618,8 @@ int nft_init(struct nft_handle *h, struct builtin_table *t) void nft_fini(struct nft_handle *h) { mnl_socket_close(h->nl); + free(mnl_nlmsg_batch_head(h->batch)); + mnl_nlmsg_batch_stop(h->batch); } int nft_table_add(struct nft_handle *h, const struct nft_table *t) -- cgit v1.2.3 From 504119fe14bffde5800a631da89b80ed6043cecb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 4 Dec 2013 12:27:37 +0100 Subject: nft: fix wrong function to release iterator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nft.c: In function ‘nft_xtables_config_load’: nft.c:2522:3: warning: passing argument 1 of ‘nft_table_list_iter_destroy’ from incompatible pointer type [enabled by default] In file included from nft.c:41:0: /usr/include/libnftables/table.h:64:6: note: expected ‘struct nft_table_list_iter *’ but argument is of type ‘struct nft_chain_list_iter *’ Introduced in (12eb85b nft: fix memory leaks in nft_xtables_config_load) but that was my fault indeed since Ana sent a v2 patch that I have overlook. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables/nft.c b/iptables/nft.c index a015947f..6f3fdb0b 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -2519,7 +2519,7 @@ err: if (titer != NULL) nft_table_list_iter_destroy(titer); if (citer != NULL) - nft_table_list_iter_destroy(citer); + nft_chain_list_iter_destroy(citer); return -1; } -- cgit v1.2.3 From d34f0611975b1da6320112df00513ba42310f356 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Fri, 13 Dec 2013 09:43:25 +0200 Subject: include: Update nftables API header in sync with kernel's one Many changes were missing. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 303 +++++++++++++++++++++++++++++++++++- 1 file changed, 296 insertions(+), 7 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 74c5f4ed..fbfd229a 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -1,6 +1,8 @@ #ifndef _LINUX_NF_TABLES_H #define _LINUX_NF_TABLES_H +#define NFT_CHAIN_MAXNAMELEN 32 + enum nft_registers { NFT_REG_VERDICT, NFT_REG_1, @@ -11,6 +13,17 @@ enum nft_registers { }; #define NFT_REG_MAX (__NFT_REG_MAX - 1) +/** + * enum nft_verdicts - nf_tables internal verdicts + * + * @NFT_CONTINUE: continue evaluation of the current rule + * @NFT_BREAK: terminate evaluation of the current rule + * @NFT_JUMP: push the current chain on the jump stack and jump to a chain + * @NFT_GOTO: jump to a chain without pushing the current chain on the jump stack + * @NFT_RETURN: return to the topmost chain on the jump stack + * + * The nf_tables verdicts share their numeric space with the netfilter verdicts. + */ enum nft_verdicts { NFT_CONTINUE = -1, NFT_BREAK = -2, @@ -19,6 +32,25 @@ enum nft_verdicts { NFT_RETURN = -5, }; +/** + * enum nf_tables_msg_types - nf_tables netlink message types + * + * @NFT_MSG_NEWTABLE: create a new table (enum nft_table_attributes) + * @NFT_MSG_GETTABLE: get a table (enum nft_table_attributes) + * @NFT_MSG_DELTABLE: delete a table (enum nft_table_attributes) + * @NFT_MSG_NEWCHAIN: create a new chain (enum nft_chain_attributes) + * @NFT_MSG_GETCHAIN: get a chain (enum nft_chain_attributes) + * @NFT_MSG_DELCHAIN: delete a chain (enum nft_chain_attributes) + * @NFT_MSG_NEWRULE: create a new rule (enum nft_rule_attributes) + * @NFT_MSG_GETRULE: get a rule (enum nft_rule_attributes) + * @NFT_MSG_DELRULE: delete a rule (enum nft_rule_attributes) + * @NFT_MSG_NEWSET: create a new set (enum nft_set_attributes) + * @NFT_MSG_GETSET: get a set (enum nft_set_attributes) + * @NFT_MSG_DELSET: delete a set (enum nft_set_attributes) + * @NFT_MSG_NEWSETELEM: create a new set element (enum nft_set_elem_attributes) + * @NFT_MSG_GETSETELEM: get a set element (enum nft_set_elem_attributes) + * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes) + */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, NFT_MSG_GETTABLE, @@ -38,6 +70,11 @@ enum nf_tables_msg_types { NFT_MSG_MAX, }; +/** + * enum nft_list_attributes - nf_tables generic list netlink attributes + * + * @NFTA_LIST_ELEM: list element (NLA_NESTED) + */ enum nft_list_attributes { NFTA_LIST_UNPEC, NFTA_LIST_ELEM, @@ -45,6 +82,12 @@ enum nft_list_attributes { }; #define NFTA_LIST_MAX (__NFTA_LIST_MAX - 1) +/** + * enum nft_hook_attributes - nf_tables netfilter hook netlink attributes + * + * @NFTA_HOOK_HOOKNUM: netfilter hook number (NLA_U32) + * @NFTA_HOOK_PRIORITY: netfilter hook priority (NLA_U32) + */ enum nft_hook_attributes { NFTA_HOOK_UNSPEC, NFTA_HOOK_HOOKNUM, @@ -62,6 +105,12 @@ enum nft_table_flags { NFT_TABLE_F_DORMANT = 0x1, }; +/** + * enum nft_table_attributes - nf_tables table netlink attributes + * + * @NFTA_TABLE_NAME: name of the table (NLA_STRING) + * @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32) + */ enum nft_table_attributes { NFTA_TABLE_UNSPEC, NFTA_TABLE_NAME, @@ -70,6 +119,18 @@ enum nft_table_attributes { }; #define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1) +/** + * enum nft_chain_attributes - nf_tables chain netlink attributes + * + * @NFTA_CHAIN_TABLE: name of the table containing the chain (NLA_STRING) + * @NFTA_CHAIN_HANDLE: numeric handle of the chain (NLA_U64) + * @NFTA_CHAIN_NAME: name of the chain (NLA_STRING) + * @NFTA_CHAIN_HOOK: hook specification for basechains (NLA_NESTED: nft_hook_attributes) + * @NFTA_CHAIN_POLICY: numeric policy of the chain (NLA_U32) + * @NFTA_CHAIN_USE: number of references to this chain (NLA_U32) + * @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING) + * @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes) + */ enum nft_chain_attributes { NFTA_CHAIN_UNSPEC, NFTA_CHAIN_TABLE, @@ -79,27 +140,49 @@ enum nft_chain_attributes { NFTA_CHAIN_POLICY, NFTA_CHAIN_USE, NFTA_CHAIN_TYPE, + NFTA_CHAIN_COUNTERS, __NFTA_CHAIN_MAX }; #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) +/** + * enum nft_rule_attributes - nf_tables rule netlink attributes + * + * @NFTA_RULE_TABLE: name of the table containing the rule (NLA_STRING) + * @NFTA_RULE_CHAIN: name of the chain containing the rule (NLA_STRING) + * @NFTA_RULE_HANDLE: numeric handle of the rule (NLA_U64) + * @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes) + * @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes) + * @NFTA_RULE_POSITION: numeric handle of the previous rule (NLA_U64) + */ enum nft_rule_attributes { NFTA_RULE_UNSPEC, NFTA_RULE_TABLE, NFTA_RULE_CHAIN, NFTA_RULE_HANDLE, NFTA_RULE_EXPRESSIONS, - NFTA_RULE_FLAGS, NFTA_RULE_COMPAT, + NFTA_RULE_POSITION, __NFTA_RULE_MAX }; #define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) +/** + * enum nft_rule_compat_flags - nf_tables rule compat flags + * + * @NFT_RULE_COMPAT_F_INV: invert the check result + */ enum nft_rule_compat_flags { NFT_RULE_COMPAT_F_INV = (1 << 1), NFT_RULE_COMPAT_F_MASK = NFT_RULE_COMPAT_F_INV, }; +/** + * enum nft_rule_compat_attributes - nf_tables rule compat attributes + * + * @NFTA_RULE_COMPAT_PROTO: numerice value of handled protocol (NLA_U32) + * @NFTA_RULE_COMPAT_FLAGS: bitmask of enum nft_rule_compat_flags (NLA_U32) + */ enum nft_rule_compat_attributes { NFTA_RULE_COMPAT_UNSPEC, NFTA_RULE_COMPAT_PROTO, @@ -209,6 +292,12 @@ enum nft_data_types { #define NFT_DATA_RESERVED_MASK 0xffffff00U +/** + * enum nft_data_attributes - nf_tables data netlink attributes + * + * @NFTA_DATA_VALUE: generic data (NLA_BINARY) + * @NFTA_DATA_VERDICT: nf_tables verdict (NLA_NESTED: nft_verdict_attributes) + */ enum nft_data_attributes { NFTA_DATA_UNSPEC, NFTA_DATA_VALUE, @@ -217,6 +306,12 @@ enum nft_data_attributes { }; #define NFTA_DATA_MAX (__NFTA_DATA_MAX - 1) +/** + * enum nft_verdict_attributes - nf_tables verdict netlink attributes + * + * @NFTA_VERDICT_CODE: nf_tables verdict (NLA_U32: enum nft_verdicts) + * @NFTA_VERDICT_CHAIN: jump target chain name (NLA_STRING) + */ enum nft_verdict_attributes { NFTA_VERDICT_UNSPEC, NFTA_VERDICT_CODE, @@ -225,6 +320,12 @@ enum nft_verdict_attributes { }; #define NFTA_VERDICT_MAX (__NFTA_VERDICT_MAX - 1) +/** + * enum nft_expr_attributes - nf_tables expression netlink attributes + * + * @NFTA_EXPR_NAME: name of the expression type (NLA_STRING) + * @NFTA_EXPR_DATA: type specific data (NLA_NESTED) + */ enum nft_expr_attributes { NFTA_EXPR_UNSPEC, NFTA_EXPR_NAME, @@ -233,6 +334,12 @@ enum nft_expr_attributes { }; #define NFTA_EXPR_MAX (__NFTA_EXPR_MAX - 1) +/** + * enum nft_immediate_attributes - nf_tables immediate expression netlink attributes + * + * @NFTA_IMMEDIATE_DREG: destination register to load data into (NLA_U32) + * @NFTA_IMMEDIATE_DATA: data to load (NLA_NESTED: nft_data_attributes) + */ enum nft_immediate_attributes { NFTA_IMMEDIATE_UNSPEC, NFTA_IMMEDIATE_DREG, @@ -241,6 +348,27 @@ enum nft_immediate_attributes { }; #define NFTA_IMMEDIATE_MAX (__NFTA_IMMEDIATE_MAX - 1) +/** + * enum nft_bitwise_attributes - nf_tables bitwise expression netlink attributes + * + * @NFTA_BITWISE_SREG: source register (NLA_U32: nft_registers) + * @NFTA_BITWISE_DREG: destination register (NLA_U32: nft_registers) + * @NFTA_BITWISE_LEN: length of operands (NLA_U32) + * @NFTA_BITWISE_MASK: mask value (NLA_NESTED: nft_data_attributes) + * @NFTA_BITWISE_XOR: xor value (NLA_NESTED: nft_data_attributes) + * + * The bitwise expression performs the following operation: + * + * dreg = (sreg & mask) ^ xor + * + * which allow to express all bitwise operations: + * + * mask xor + * NOT: 1 1 + * OR: 0 x + * XOR: 1 x + * AND: x 0 + */ enum nft_bitwise_attributes { NFTA_BITWISE_UNSPEC, NFTA_BITWISE_SREG, @@ -252,11 +380,26 @@ enum nft_bitwise_attributes { }; #define NFTA_BITWISE_MAX (__NFTA_BITWISE_MAX - 1) +/** + * enum nft_byteorder_ops - nf_tables byteorder operators + * + * @NFT_BYTEORDER_NTOH: network to host operator + * @NFT_BYTEORDER_HTON: host to network opertaor + */ enum nft_byteorder_ops { NFT_BYTEORDER_NTOH, NFT_BYTEORDER_HTON, }; +/** + * enum nft_byteorder_attributes - nf_tables byteorder expression netlink attributes + * + * @NFTA_BYTEORDER_SREG: source register (NLA_U32: nft_registers) + * @NFTA_BYTEORDER_DREG: destination register (NLA_U32: nft_registers) + * @NFTA_BYTEORDER_OP: operator (NLA_U32: enum nft_byteorder_ops) + * @NFTA_BYTEORDER_LEN: length of the data (NLA_U32) + * @NFTA_BYTEORDER_SIZE: data size in bytes (NLA_U32: 2 or 4) + */ enum nft_byteorder_attributes { NFTA_BYTEORDER_UNSPEC, NFTA_BYTEORDER_SREG, @@ -268,6 +411,16 @@ enum nft_byteorder_attributes { }; #define NFTA_BYTEORDER_MAX (__NFTA_BYTEORDER_MAX - 1) +/** + * enum nft_cmp_ops - nf_tables relational operator + * + * @NFT_CMP_EQ: equal + * @NFT_CMP_NEQ: not equal + * @NFT_CMP_LT: less than + * @NFT_CMP_LTE: less than or equal to + * @NFT_CMP_GT: greater than + * @NFT_CMP_GTE: greater than or equal to + */ enum nft_cmp_ops { NFT_CMP_EQ, NFT_CMP_NEQ, @@ -277,6 +430,13 @@ enum nft_cmp_ops { NFT_CMP_GTE, }; +/** + * enum nft_cmp_attributes - nf_tables cmp expression netlink attributes + * + * @NFTA_CMP_SREG: source register of data to compare (NLA_U32: nft_registers) + * @NFTA_CMP_OP: cmp operation (NLA_U32: nft_cmp_ops) + * @NFTA_CMP_DATA: data to compare against (NLA_NESTED: nft_data_attributes) + */ enum nft_cmp_attributes { NFTA_CMP_UNSPEC, NFTA_CMP_SREG, @@ -286,6 +446,13 @@ enum nft_cmp_attributes { }; #define NFTA_CMP_MAX (__NFTA_CMP_MAX - 1) +/** + * enum nft_lookup_attributes - nf_tables set lookup expression netlink attributes + * + * @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING) + * @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers) + * @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers) + */ enum nft_lookup_attributes { NFTA_LOOKUP_UNSPEC, NFTA_LOOKUP_SET, @@ -295,12 +462,27 @@ enum nft_lookup_attributes { }; #define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1) +/** + * enum nft_payload_bases - nf_tables payload expression offset bases + * + * @NFT_PAYLOAD_LL_HEADER: link layer header + * @NFT_PAYLOAD_NETWORK_HEADER: network header + * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header + */ enum nft_payload_bases { NFT_PAYLOAD_LL_HEADER, NFT_PAYLOAD_NETWORK_HEADER, NFT_PAYLOAD_TRANSPORT_HEADER, }; +/** + * enum nft_payload_attributes - nf_tables payload expression netlink attributes + * + * @NFTA_PAYLOAD_DREG: destination register to load data into (NLA_U32: nft_registers) + * @NFTA_PAYLOAD_BASE: payload base (NLA_U32: nft_payload_bases) + * @NFTA_PAYLOAD_OFFSET: payload offset relative to base (NLA_U32) + * @NFTA_PAYLOAD_LEN: payload length (NLA_U32) + */ enum nft_payload_attributes { NFTA_PAYLOAD_UNSPEC, NFTA_PAYLOAD_DREG, @@ -311,6 +493,14 @@ enum nft_payload_attributes { }; #define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1) +/** + * enum nft_exthdr_attributes - nf_tables IPv6 extension header expression netlink attributes + * + * @NFTA_EXTHDR_DREG: destination register (NLA_U32: nft_registers) + * @NFTA_EXTHDR_TYPE: extension header type (NLA_U8) + * @NFTA_EXTHDR_OFFSET: extension header offset (NLA_U32) + * @NFTA_EXTHDR_LEN: extension header length (NLA_U32) + */ enum nft_exthdr_attributes { NFTA_EXTHDR_UNSPEC, NFTA_EXTHDR_DREG, @@ -321,6 +511,25 @@ enum nft_exthdr_attributes { }; #define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1) +/** + * enum nft_meta_keys - nf_tables meta expression keys + * + * @NFT_META_LEN: packet length (skb->len) + * @NFT_META_PROTOCOL: packet ethertype protocol (skb->protocol), invalid in OUTPUT + * @NFT_META_PRIORITY: packet priority (skb->priority) + * @NFT_META_MARK: packet mark (skb->mark) + * @NFT_META_IIF: packet input interface index (dev->ifindex) + * @NFT_META_OIF: packet output interface index (dev->ifindex) + * @NFT_META_IIFNAME: packet input interface name (dev->name) + * @NFT_META_OIFNAME: packet output interface name (dev->name) + * @NFT_META_IIFTYPE: packet input interface type (dev->type) + * @NFT_META_OIFTYPE: packet output interface type (dev->type) + * @NFT_META_SKUID: originating socket UID (fsuid) + * @NFT_META_SKGID: originating socket GID (fsgid) + * @NFT_META_NFTRACE: packet nftrace bit + * @NFT_META_RTCLASSID: realm value of packet's route (skb->dst->tclassid) + * @NFT_META_SECMARK: packet secmark (skb->secmark) + */ enum nft_meta_keys { NFT_META_LEN, NFT_META_PROTOCOL, @@ -339,6 +548,12 @@ enum nft_meta_keys { NFT_META_SECMARK, }; +/** + * enum nft_meta_attributes - nf_tables meta expression netlink attributes + * + * @NFTA_META_DREG: destination register (NLA_U32) + * @NFTA_META_KEY: meta data item to load (NLA_U32: nft_meta_keys) + */ enum nft_meta_attributes { NFTA_META_UNSPEC, NFTA_META_DREG, @@ -347,6 +562,23 @@ enum nft_meta_attributes { }; #define NFTA_META_MAX (__NFTA_META_MAX - 1) +/** + * enum nft_ct_keys - nf_tables ct expression keys + * + * @NFT_CT_STATE: conntrack state (bitmask of enum ip_conntrack_info) + * @NFT_CT_DIRECTION: conntrack direction (enum ip_conntrack_dir) + * @NFT_CT_STATUS: conntrack status (bitmask of enum ip_conntrack_status) + * @NFT_CT_MARK: conntrack mark value + * @NFT_CT_SECMARK: conntrack secmark value + * @NFT_CT_EXPIRATION: relative conntrack expiration time in ms + * @NFT_CT_HELPER: connection tracking helper assigned to conntrack + * @NFT_CT_L3PROTOCOL: conntrack layer 3 protocol + * @NFT_CT_SRC: conntrack layer 3 protocol source (IPv4/IPv6 address) + * @NFT_CT_DST: conntrack layer 3 protocol destination (IPv4/IPv6 address) + * @NFT_CT_PROTOCOL: conntrack layer 4 protocol + * @NFT_CT_PROTO_SRC: conntrack layer 4 protocol source + * @NFT_CT_PROTO_DST: conntrack layer 4 protocol destination + */ enum nft_ct_keys { NFT_CT_STATE, NFT_CT_DIRECTION, @@ -355,13 +587,21 @@ enum nft_ct_keys { NFT_CT_SECMARK, NFT_CT_EXPIRATION, NFT_CT_HELPER, - NFT_CT_PROTOCOL, + NFT_CT_L3PROTOCOL, NFT_CT_SRC, NFT_CT_DST, + NFT_CT_PROTOCOL, NFT_CT_PROTO_SRC, NFT_CT_PROTO_DST, }; +/** + * enum nft_ct_attributes - nf_tables ct expression netlink attributes + * + * @NFTA_CT_DREG: destination register (NLA_U32) + * @NFTA_CT_KEY: conntrack data item to load (NLA_U32: nft_ct_keys) + * @NFTA_CT_DIRECTION: direction in case of directional keys (NLA_U8) + */ enum nft_ct_attributes { NFTA_CT_UNSPEC, NFTA_CT_DREG, @@ -371,14 +611,26 @@ enum nft_ct_attributes { }; #define NFTA_CT_MAX (__NFTA_CT_MAX - 1) +/** + * enum nft_limit_attributes - nf_tables limit expression netlink attributes + * + * @NFTA_LIMIT_RATE: refill rate (NLA_U64) + * @NFTA_LIMIT_UNIT: refill unit (NLA_U64) + */ enum nft_limit_attributes { NFTA_LIMIT_UNSPEC, NFTA_LIMIT_RATE, - NFTA_LIMIT_DEPTH, + NFTA_LIMIT_UNIT, __NFTA_LIMIT_MAX }; #define NFTA_LIMIT_MAX (__NFTA_LIMIT_MAX - 1) +/** + * enum nft_counter_attributes - nf_tables counter expression netlink attributes + * + * @NFTA_COUNTER_BYTES: number of bytes (NLA_U64) + * @NFTA_COUNTER_PACKETS: number of packets (NLA_U64) + */ enum nft_counter_attributes { NFTA_COUNTER_UNSPEC, NFTA_COUNTER_BYTES, @@ -387,6 +639,14 @@ enum nft_counter_attributes { }; #define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1) +/** + * enum nft_log_attributes - nf_tables log expression netlink attributes + * + * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32) + * @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING) + * @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32) + * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32) + */ enum nft_log_attributes { NFTA_LOG_UNSPEC, NFTA_LOG_GROUP, @@ -397,11 +657,23 @@ enum nft_log_attributes { }; #define NFTA_LOG_MAX (__NFTA_LOG_MAX - 1) +/** + * enum nft_reject_types - nf_tables reject expression reject types + * + * @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable + * @NFT_REJECT_TCP_RST: reject using TCP RST + */ enum nft_reject_types { NFT_REJECT_ICMP_UNREACH, NFT_REJECT_TCP_RST, }; +/** + * enum nft_reject_attributes - nf_tables reject expression netlink attributes + * + * @NFTA_REJECT_TYPE: packet type to use (NLA_U32: nft_reject_types) + * @NFTA_REJECT_ICMP_CODE: ICMP code to use (NLA_U8) + */ enum nft_reject_attributes { NFTA_REJECT_UNSPEC, NFTA_REJECT_TYPE, @@ -410,18 +682,35 @@ enum nft_reject_attributes { }; #define NFTA_REJECT_MAX (__NFTA_REJECT_MAX - 1) +/** + * enum nft_nat_types - nf_tables nat expression NAT types + * + * @NFT_NAT_SNAT: source NAT + * @NFT_NAT_DNAT: destination NAT + */ enum nft_nat_types { NFT_NAT_SNAT, NFT_NAT_DNAT, }; +/** + * enum nft_nat_attributes - nf_tables nat expression netlink attributes + * + * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types) + * @NFTA_NAT_FAMILY: NAT family (NLA_U32) + * @NFTA_NAT_REG_ADDR_MIN: source register of address range start (NLA_U32: nft_registers) + * @NFTA_NAT_REG_ADDR_MAX: source register of address range end (NLA_U32: nft_registers) + * @NFTA_NAT_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) + * @NFTA_NAT_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) + */ enum nft_nat_attributes { NFTA_NAT_UNSPEC, NFTA_NAT_TYPE, - NFTA_NAT_ADDR_MIN, - NFTA_NAT_ADDR_MAX, - NFTA_NAT_PROTO_MIN, - NFTA_NAT_PROTO_MAX, + NFTA_NAT_FAMILY, + NFTA_NAT_REG_ADDR_MIN, + NFTA_NAT_REG_ADDR_MAX, + NFTA_NAT_REG_PROTO_MIN, + NFTA_NAT_REG_PROTO_MAX, __NFTA_NAT_MAX }; #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) -- cgit v1.2.3 From 4182478977685b0cbe71eb3d75f004f23a775d6c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 10 Jan 2014 14:02:22 +0100 Subject: nft: fix inconsistent data type in NFT_EXPR_CMP_OP and NFT_EXPR_META_KEY We have to use uint32_t instead uint8_t to adapt this to the libnftables changes. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 2 +- iptables/nft-shared.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 1afe8b66..3aeb8a72 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -131,7 +131,7 @@ static void get_frag(struct nft_rule_expr_iter *iter, bool *inv) return; } - op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); + op = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP); if (op == NFT_CMP_EQ) *inv = true; else diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index e0eaa170..43f03b20 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -90,8 +90,8 @@ void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len) if (expr == NULL) return; - nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_SREG, NFT_REG_1); - nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_OP, op); + nft_rule_expr_set_u32(expr, NFT_EXPR_CMP_SREG, NFT_REG_1); + nft_rule_expr_set_u32(expr, NFT_EXPR_CMP_OP, op); nft_rule_expr_set(expr, NFT_EXPR_CMP_DATA, data, len); nft_rule_add_expr(r, expr); @@ -225,7 +225,7 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, switch(key) { case NFT_META_IIF: value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); - if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) *invflags |= IPT_INV_VIA_IN; if_indextoname(value, iniface); @@ -234,7 +234,7 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, break; case NFT_META_OIF: value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); - if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) *invflags |= IPT_INV_VIA_OUT; if_indextoname(value, outiface); @@ -243,7 +243,7 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, break; case NFT_META_IIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); - if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) *invflags |= IPT_INV_VIA_IN; memcpy(iniface, ifname, len); @@ -258,7 +258,7 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, break; case NFT_META_OIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); - if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) + if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) *invflags |= IPT_INV_VIA_OUT; memcpy(outiface, ifname, len); @@ -372,7 +372,7 @@ void get_cmp_data(struct nft_rule_expr_iter *iter, } memcpy(data, nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len), dlen); - op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); + op = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP); if (op == NFT_CMP_NEQ) *inv = true; else @@ -383,7 +383,7 @@ void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, void *data) { - uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); + uint8_t key = nft_rule_expr_get_u32(e, NFT_EXPR_META_KEY); struct nft_family_ops *ops = nft_family_ops_lookup(family); const char *name; -- cgit v1.2.3 From a4e1098169a67716a81316c36ce22ddcb33df1c0 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Mon, 20 Jan 2014 17:56:41 +0200 Subject: nft: Use new libnftnl library name against former libnftables Adapt the current code to use the new library name libnftnl. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- configure.ac | 4 ++-- iptables/Makefile.am | 6 +++--- iptables/nft-shared.c | 4 ++-- iptables/nft-shared.h | 4 ++-- iptables/nft.c | 8 ++++---- iptables/xtables-config-parser.y | 4 ++-- iptables/xtables-events.c | 6 +++--- iptables/xtables-restore.c | 2 +- iptables/xtables-save.c | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/configure.ac b/configure.ac index cbbf66df..ac7c9c00 100644 --- a/configure.ac +++ b/configure.ac @@ -123,9 +123,9 @@ PKG_CHECK_MODULES([libmnl], [libmnl >= 1.0], [mnl=1], [mnl=0]) AM_CONDITIONAL([HAVE_LIBMNL], [test "$mnl" = 1]) -PKG_CHECK_MODULES([libnftables], [libnftables >= 1.0], +PKG_CHECK_MODULES([libnftnl], [libnftnl >= 1.0], [nftables=1], [nftables=0]) -AM_CONDITIONAL([HAVE_LIBNFTABLES], [test "$nftables" = 1]) +AM_CONDITIONAL([HAVE_LIBNFTNL], [test "$nftables" = 1]) AM_PROG_LEX AC_PROG_YACC diff --git a/iptables/Makefile.am b/iptables/Makefile.am index b9a9743e..2c6a163f 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -1,7 +1,7 @@ # -*- Makefile -*- AM_CFLAGS = ${regular_CFLAGS} -AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS} ${libmnl_CFLAGS} ${libnftables_CFLAGS} +AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS} ${libmnl_CFLAGS} ${libnftnl_CFLAGS} AM_YFLAGS = -d xtables_multi_SOURCES = xtables-multi.c iptables-xml.c @@ -27,14 +27,14 @@ xtables_multi_LDADD += ../libxtables/libxtables.la -lm if ENABLE_NFTABLES if HAVE_LIBMNL -if HAVE_LIBNFTABLES +if HAVE_LIBNFTNL xtables_multi_SOURCES += xtables-config-parser.y xtables-config-syntax.l xtables_multi_SOURCES += xtables-save.c xtables-restore.c \ xtables-standalone.c xtables.c nft.c \ nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \ xtables-config.c xtables-events.c \ xtables-arp-standalone.c xtables-arp.c -xtables_multi_LDADD += -lmnl -lnftables ${libmnl_LIBS} ${libnftables_LIBS} +xtables_multi_LDADD += ${libmnl_LIBS} ${libnftnl_LIBS} xtables_multi_CFLAGS += -DENABLE_NFTABLES # yacc and lex generate dirty code xtables_multi-xtables-config-parser.o xtables_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 43f03b20..d0e28ba9 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -22,8 +22,8 @@ #include #include -#include -#include +#include +#include #include "nft-shared.h" #include "xshared.h" diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 9df17bc3..1d01ebad 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include "xshared.h" diff --git a/iptables/nft.c b/iptables/nft.c index 6f3fdb0b..fc9db998 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -38,10 +38,10 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include /* inet_ntoa */ #include diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y index 36dae38d..2770a1b9 100644 --- a/iptables/xtables-config-parser.y +++ b/iptables/xtables-config-parser.y @@ -17,8 +17,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/iptables/xtables-events.c b/iptables/xtables-events.c index 408e091f..4be8ab8c 100644 --- a/iptables/xtables-events.c +++ b/iptables/xtables-events.c @@ -19,9 +19,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include "iptables.h" /* for xtables_globals */ diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 06053f62..9a80f1ef 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -16,7 +16,7 @@ #include "libiptc/libiptc.h" #include "xtables-multi.h" #include "nft.h" -#include +#include #ifdef DEBUG #define DEBUGP(x, args...) fprintf(stderr, x, ## args) diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 990c0fe6..77eab149 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -19,7 +19,7 @@ #include "xtables-multi.h" #include "nft.h" -#include +#include #ifndef NO_SHARED_LIBS #include -- cgit v1.2.3 From 4cce0ad2585270cdb901444f7a28259e89d7bfdc Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 4 Feb 2014 13:17:30 +0100 Subject: configure: fix wrong reference to the conntrack-tools Reported-by: Jan Engelhardt Signed-off-by: Pablo Neira Ayuso --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index ac7c9c00..3012df4c 100644 --- a/configure.ac +++ b/configure.ac @@ -154,8 +154,8 @@ if test "$flex_major" -eq "2" && test "$flex_minor" -eq "5" && test "$flex_rev" else AC_MSG_WARN([flex version $flex_version found. Version 2.5.33 or greater is required. You may experience problems - while compilating the conntrack-tools. Please, consider to upgrade - flex.]) + while compilating the nftables compatibility layer for iptables. + Please, consider to upgrade flex.]) fi regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \ -- cgit v1.2.3 From 9e48e5071a8fdb6970c28b9edfcadb8bc0fc7a1e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 4 Feb 2014 13:20:09 +0100 Subject: configure: rename --disable-xtables to --disable-nftables The compat utility name is xtables, the name used for the compatibility binary. Better explicitly refer to the compatibility later as nftables-compat. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3012df4c..54f1fd5b 100644 --- a/configure.ac +++ b/configure.ac @@ -61,7 +61,7 @@ AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) AC_ARG_ENABLE([nftables], - AS_HELP_STRING([--disable-xtables], [Do not build xtables]), + AS_HELP_STRING([--disable-nftables], [Do not build nftables compat]), [enable_nftables="$enableval"], [enable_nftables="yes"]) libiptc_LDFLAGS2=""; -- cgit v1.2.3 From 38e5ce647766142a2ae3f47f32ae40e55c43cf3d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 4 Feb 2014 13:32:58 +0100 Subject: configure: conditional dependencies for nftables-compat Depend on libmnl, libnftnl, flex and bison if only if the nftables compatibility is enabled. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 73 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/configure.ac b/configure.ac index 54f1fd5b..2521ccc8 100644 --- a/configure.ac +++ b/configure.ac @@ -119,44 +119,45 @@ PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0], [nfnetlink=1], [nfnetlink=0]) AM_CONDITIONAL([HAVE_LIBNFNETLINK], [test "$nfnetlink" = 1]) -PKG_CHECK_MODULES([libmnl], [libmnl >= 1.0], - [mnl=1], [mnl=0]) -AM_CONDITIONAL([HAVE_LIBMNL], [test "$mnl" = 1]) - -PKG_CHECK_MODULES([libnftnl], [libnftnl >= 1.0], - [nftables=1], [nftables=0]) -AM_CONDITIONAL([HAVE_LIBNFTNL], [test "$nftables" = 1]) - -AM_PROG_LEX -AC_PROG_YACC - -if test -z "$ac_cv_prog_YACC" -then - echo "*** Error: No suitable bison/yacc found. ***" - echo " Please install the 'bison' package." - exit 1 -fi -if test -z "$ac_cv_prog_LEX" -then - echo "*** Error: No suitable flex/lex found. ***" - echo " Please install the 'flex' package." - exit 1 +if test "x$enable_nftables" = "xyes"; then + PKG_CHECK_MODULES([libmnl], [libmnl >= 1.0], [mnl=1], [mnl=0]) + + PKG_CHECK_MODULES([libnftnl], [libnftnl >= 1.0], [nftables=1], [nftables=0]) + + AM_PROG_LEX + AC_PROG_YACC + + if test -z "$ac_cv_prog_YACC" + then + echo "*** Error: No suitable bison/yacc found. ***" + echo " Please install the 'bison' package." + exit 1 + fi + if test -z "$ac_cv_prog_LEX" + then + echo "*** Error: No suitable flex/lex found. ***" + echo " Please install the 'flex' package." + exit 1 + fi + + AC_MSG_CHECKING(flex version) + flex_version=`$ac_cv_prog_LEX --version | sed 's/version//g' | awk '/flex/ {print $2}'` + flex_major=`echo $flex_version| cut -d . -f 1` + flex_minor=`echo $flex_version| cut -d . -f 2` + flex_rev=`echo $flex_version| cut -d . -f 3` + + if test "$flex_major" -eq "2" && test "$flex_minor" -eq "5" && test "$flex_rev" -ge "33"; then + AC_MSG_RESULT([$flex_version. OK]) + else + AC_MSG_WARN([flex version $flex_version found. + Version 2.5.33 or greater is required. You may experience problems + while compilating the nftables compatibility layer for iptables. + Please, consider to upgrade flex.]) + fi fi -AC_MSG_CHECKING(flex version) -flex_version=`$ac_cv_prog_LEX --version | sed 's/version//g' | awk '/flex/ {print $2}'` -flex_major=`echo $flex_version| cut -d . -f 1` -flex_minor=`echo $flex_version| cut -d . -f 2` -flex_rev=`echo $flex_version| cut -d . -f 3` - -if test "$flex_major" -eq "2" && test "$flex_minor" -eq "5" && test "$flex_rev" -ge "33"; then - AC_MSG_RESULT([$flex_version. OK]) -else - AC_MSG_WARN([flex version $flex_version found. - Version 2.5.33 or greater is required. You may experience problems - while compilating the nftables compatibility layer for iptables. - Please, consider to upgrade flex.]) -fi +AM_CONDITIONAL([HAVE_LIBMNL], [test "$mnl" = 1]) +AM_CONDITIONAL([HAVE_LIBNFTNL], [test "$nftables" = 1]) regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \ -Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \ -- cgit v1.2.3 From 43bb2819c5b7b783cbaceffd0e6d4b6e502a0fb5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 4 Feb 2014 16:18:55 +0100 Subject: xtables-restore: remove dependency with libip4tc Add a new operation structure, we don't actually need the libip4tc definition. Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-restore.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 9a80f1ef..230894cd 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -161,6 +161,10 @@ static void add_param_to_argv(char *parsestart) } } +static const struct xtc_ops xtc_ops = { + .strerror = nft_strerror, +}; + int xtables_restore_main(int argc, char *argv[]) { @@ -173,7 +177,7 @@ xtables_restore_main(int argc, char *argv[]) FILE *in; int in_table = 0, testing = 0; const char *tablename = NULL; - const struct xtc_ops *ops = &iptc_ops; + const struct xtc_ops *ops = &xtc_ops; struct nft_chain_list *chain_list; struct nft_chain *chain_obj; -- cgit v1.2.3 From 4cffe00557b40dfe8c3236746797b24c4074c95e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 4 Feb 2014 16:21:18 +0100 Subject: xtables: add xtables-compat-multi for the nftables compatibility layer This patch should allow distributors to switch to the iptables over nftables compatibility layer in a transparent way by updating symbolic links from: lrwxrwxrwx 1 root root 13 feb 4 15:35 iptables -> xtables-multi to: lrwxrwxrwx 1 root root 13 feb 4 15:35 iptables -> xtables-compat-multi Same thing with iptables-save, iptables-restore, ip6tables, ip6tables-save, ip6tables-restore and arptables. Note that, after this patch, the following new symlinks are installed: * iptables-compat * iptables-compat-save * iptables-compat-restore * ip6tables-compat * ip6tables-compat-save * ip6tables-compat-restore * arptables-compat which point to the new binary xtables-compat-multi. The idea is to keep both native and compatibility tools installed in the system, which should also make it easier for testing purposes. The iptables over nftables compatibility layer is enabled by default and it requires the libmnl and libnftnl libraries. If you don't want to compile the compatibility layer, you can still disable it through --disable-nftables. This patch also includes changes to adapt the existing code to this approach. Signed-off-by: Pablo Neira Ayuso --- iptables/Makefile.am | 41 ++++++++++++++++++++++++--------------- iptables/xtables-arp-standalone.c | 2 +- iptables/xtables-arp.c | 2 +- iptables/xtables-compat-multi.c | 39 +++++++++++++++++++++++++++++++++++++ iptables/xtables-multi.h | 13 +++++++++---- iptables/xtables-restore.c | 22 ++++++++++++++++----- iptables/xtables-save.c | 18 +++++++++++++---- iptables/xtables-standalone.c | 24 ++++++++++++++++------- 8 files changed, 123 insertions(+), 38 deletions(-) create mode 100644 iptables/xtables-compat-multi.c diff --git a/iptables/Makefile.am b/iptables/Makefile.am index 2c6a163f..41bca7c7 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -25,24 +25,32 @@ endif xtables_multi_SOURCES += xshared.c xtables_multi_LDADD += ../libxtables/libxtables.la -lm +# nftables compatibility layer if ENABLE_NFTABLES -if HAVE_LIBMNL -if HAVE_LIBNFTNL -xtables_multi_SOURCES += xtables-config-parser.y xtables-config-syntax.l -xtables_multi_SOURCES += xtables-save.c xtables-restore.c \ - xtables-standalone.c xtables.c nft.c \ - nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \ - xtables-config.c xtables-events.c \ - xtables-arp-standalone.c xtables-arp.c -xtables_multi_LDADD += ${libmnl_LIBS} ${libnftnl_LIBS} -xtables_multi_CFLAGS += -DENABLE_NFTABLES -# yacc and lex generate dirty code -xtables_multi-xtables-config-parser.o xtables_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls -endif +xtables_compat_multi_SOURCES = xtables-compat-multi.c iptables-xml.c +xtables_compat_multi_CFLAGS = ${AM_CFLAGS} +xtables_compat_multi_LDADD = ../extensions/libext.a +if ENABLE_STATIC +xtables_compat_multi_CFLAGS += -DALL_INCLUSIVE endif +xtables_compat_multi_CFLAGS += -DENABLE_NFTABLES -DENABLE_IPV4 -DENABLE_IPV6 +xtables_compat_multi_SOURCES += xtables-config-parser.y xtables-config-syntax.l +xtables_compat_multi_SOURCES += xtables-save.c xtables-restore.c \ + xtables-standalone.c xtables.c nft.c \ + nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \ + xtables-config.c xtables-events.c \ + xtables-arp-standalone.c xtables-arp.c +xtables_compat_multi_LDADD += ${libmnl_LIBS} ${libnftnl_LIBS} +# yacc and lex generate dirty code +xtables_compat_multi-xtables-config-parser.o xtables_compat_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls +xtables_compat_multi_SOURCES += xshared.c +xtables_compat_multi_LDADD += ../libxtables/libxtables.la -lm endif sbin_PROGRAMS = xtables-multi +if ENABLE_NFTABLES +sbin_PROGRAMS += xtables-compat-multi +endif man_MANS = iptables.8 iptables-restore.8 iptables-save.8 \ iptables-xml.1 ip6tables.8 ip6tables-restore.8 \ ip6tables-save.8 iptables-extensions.8 @@ -57,8 +65,9 @@ if ENABLE_IPV6 v6_sbin_links = ip6tables ip6tables-restore ip6tables-save endif if ENABLE_NFTABLES -x_sbin_links = xtables xtables-restore xtables-save xtables-config \ - xtables-events xtables-arp +x_sbin_links = iptables-compat iptables-compat-restore iptables-compat-save \ + ip6tables-compat ip6tables-compat-restore ip6tables-compat-save \ + arptables-compat xtables-config xtables-events endif iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man @@ -75,4 +84,4 @@ install-exec-hook: for i in ${vx_bin_links}; do ${LN_S} -f "${sbindir}/xtables-multi" "${DESTDIR}${bindir}/$$i"; done; for i in ${v4_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done; for i in ${v6_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done; - for i in ${x_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done; + for i in ${x_sbin_links}; do ${LN_S} -f xtables-compat-multi "${DESTDIR}${sbindir}/$$i"; done; diff --git a/iptables/xtables-arp-standalone.c b/iptables/xtables-arp-standalone.c index 8d4679f1..23b6bcb4 100644 --- a/iptables/xtables-arp-standalone.c +++ b/iptables/xtables-arp-standalone.c @@ -62,7 +62,7 @@ int xtables_arp_main(int argc, char *argv[]) .family = NFPROTO_ARP, }; - xtables_globals.program_name = "xtables-arp"; + xtables_globals.program_name = "arptables"; /* This code below could be replaced by xtables_init_all, which * doesn't support NFPROTO_ARP yet. */ diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c index 298801b3..0c79a387 100644 --- a/iptables/xtables-arp.c +++ b/iptables/xtables-arp.c @@ -202,7 +202,7 @@ static int inverse_for_options[NUMBER_OF_OPT] = }; const char *program_version = XTABLES_VERSION; -const char *program_name = "xtables-arp"; +const char *program_name = "arptables"; /* A few hardcoded protocols for 'all' and in case the user has no /etc/protocols */ diff --git a/iptables/xtables-compat-multi.c b/iptables/xtables-compat-multi.c new file mode 100644 index 00000000..47810524 --- /dev/null +++ b/iptables/xtables-compat-multi.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include "xshared.h" + +#include "xtables-multi.h" + +static const struct subcommand multi_subcommands[] = { + {"iptables-xml", iptables_xml_main}, + {"xml", iptables_xml_main}, + {"iptables", xtables_ip4_main}, + {"iptables-compat", xtables_ip4_main}, + {"main4", xtables_ip4_main}, + {"save4", xtables_ip4_save_main}, + {"restore4", xtables_ip4_restore_main}, + {"iptables-save", xtables_ip4_save_main}, + {"iptables-restore", xtables_ip4_restore_main}, + {"iptables-compat-save", xtables_ip4_save_main}, + {"iptables-compat-restore", xtables_ip4_restore_main}, + {"ip6tables", xtables_ip6_main}, + {"ip6tables-compat", xtables_ip6_main}, + {"main6", xtables_ip6_main}, + {"save6", xtables_ip6_save_main}, + {"restore6", xtables_ip6_restore_main}, + {"ip6tables-save", xtables_ip6_save_main}, + {"ip6tables-restore", xtables_ip6_restore_main}, + {"ip6tables-compat-save", xtables_ip6_save_main}, + {"ip6tables-compat-restore", xtables_ip6_restore_main}, + {"arptables", xtables_arp_main}, + {"arptables-compat", xtables_arp_main}, + {"xtables-config", xtables_config_main}, + {"xtables-events", xtables_events_main}, + {NULL}, +}; + +int main(int argc, char **argv) +{ + return subcmd_main(argc, argv, multi_subcommands); +} diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h index 759e24f0..e706894b 100644 --- a/iptables/xtables-multi.h +++ b/iptables/xtables-multi.h @@ -2,11 +2,16 @@ #define _XTABLES_MULTI_H 1 extern int iptables_xml_main(int, char **); -extern int xtables_main(int, char **); -extern int xtables_save_main(int, char **); -extern int xtables_restore_main(int, char **); +#ifdef ENABLE_NFTABLES +extern int xtables_ip4_main(int, char **); +extern int xtables_ip4_save_main(int, char **); +extern int xtables_ip4_restore_main(int, char **); +extern int xtables_ip6_main(int, char **); +extern int xtables_ip6_save_main(int, char **); +extern int xtables_ip6_restore_main(int, char **); +extern int xtables_arp_main(int, char **); extern int xtables_config_main(int, char **); extern int xtables_events_main(int, char **); -extern int xtables_arp_main(int, char **); +#endif #endif /* _XTABLES_MULTI_H */ diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 230894cd..c4af2c5d 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -165,11 +165,11 @@ static const struct xtc_ops xtc_ops = { .strerror = nft_strerror, }; -int -xtables_restore_main(int argc, char *argv[]) +static int +xtables_restore_main(int family, const char *progname, int argc, char *argv[]) { struct nft_handle h = { - .family = AF_INET, /* default to IPv4 */ + .family = family, }; char buffer[10240]; int c; @@ -183,8 +183,8 @@ xtables_restore_main(int argc, char *argv[]) line = 0; - xtables_globals.program_name = "xtables-restore"; - c = xtables_init_all(&xtables_globals, NFPROTO_IPV4); + xtables_globals.program_name = progname; + c = xtables_init_all(&xtables_globals, family); if (c < 0) { fprintf(stderr, "%s/%s Failed to initialize xtables\n", xtables_globals.program_name, @@ -472,3 +472,15 @@ xtables_restore_main(int argc, char *argv[]) fclose(in); return 0; } + +int xtables_ip4_restore_main(int argc, char *argv[]) +{ + return xtables_restore_main(NFPROTO_IPV4, "iptables-restore", + argc, argv); +} + +int xtables_ip6_restore_main(int argc, char *argv[]) +{ + return xtables_restore_main(NFPROTO_IPV6, "ip6tables-restore", + argc, argv); +} diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 77eab149..93065cfc 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -74,17 +74,17 @@ do_output(struct nft_handle *h, const char *tablename, bool counters) * :Chain name POLICY packets bytes * rule */ -int -xtables_save_main(int argc, char *argv[]) +static int +xtables_save_main(int family, const char *progname, int argc, char *argv[]) { const char *tablename = NULL; bool dump = false; struct nft_handle h = { - .family = AF_INET, /* default to AF_INET */ + .family = family, }; int c; - xtables_globals.program_name = "xtables-save"; + xtables_globals.program_name = progname; /* XXX xtables_init_all does several things we don't want */ c = xtables_init_all(&xtables_globals, NFPROTO_IPV4); if (c < 0) { @@ -143,3 +143,13 @@ xtables_save_main(int argc, char *argv[]) return !do_output(&h, tablename, show_counters); } + +int xtables_ip4_save_main(int argc, char *argv[]) +{ + return xtables_save_main(NFPROTO_IPV4, "iptables-save", argc, argv); +} + +int xtables_ip6_save_main(int argc, char *argv[]) +{ + return xtables_save_main(NFPROTO_IPV6, "ip6tables-save", argc, argv); +} diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index c9f8e15a..eb139805 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -39,17 +39,17 @@ #include "xtables-multi.h" #include "nft.h" -int -xtables_main(int argc, char *argv[]) +static int +xtables_main(int family, const char *progname, int argc, char *argv[]) { int ret; char *table = "filter"; - struct nft_handle h; + struct nft_handle h = { + .family = family, + }; - memset(&h, 0, sizeof(h)); - - xtables_globals.program_name = "xtables"; - ret = xtables_init_all(&xtables_globals, NFPROTO_IPV4); + xtables_globals.program_name = progname; + ret = xtables_init_all(&xtables_globals, family); if (ret < 0) { fprintf(stderr, "%s/%s Failed to initialize xtables\n", xtables_globals.program_name, @@ -92,3 +92,13 @@ xtables_main(int argc, char *argv[]) exit(!ret); } + +int xtables_ip4_main(int argc, char *argv[]) +{ + return xtables_main(NFPROTO_IPV4, "iptables", argc, argv); +} + +int xtables_ip6_main(int argc, char *argv[]) +{ + return xtables_main(NFPROTO_IPV6, "ip6tables", argc, argv); +} -- cgit v1.2.3 From 7851975e5055381d30f0788d90671485695928e1 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 11 Feb 2014 12:46:44 +0200 Subject: xtables: Add backward compatibility with -w option Just to keep aligned with iptables legacy tool. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.h | 2 +- iptables/xtables-restore.c | 3 ++- iptables/xtables-standalone.c | 2 +- iptables/xtables.c | 11 ++++++++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/iptables/nft.h b/iptables/nft.h index 26b60b99..22af66e2 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -135,7 +135,7 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt); const char *nft_strerror(int err); /* For xtables.c */ -int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table); +int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table, bool restore); /* For xtables-arptables.c */ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table); diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index c4af2c5d..730800ff 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -442,7 +442,8 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[]) for (a = 0; a < newargc; a++) DEBUGP("argv[%u]: %s\n", a, newargv[a]); - ret = do_commandx(&h, newargc, newargv, &newargv[2]); + ret = do_commandx(&h, newargc, newargv, + &newargv[2], true); if (ret < 0) { ret = nft_abort(&h); if (ret < 0) { diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index eb139805..355a4460 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -70,7 +70,7 @@ xtables_main(int family, const char *progname, int argc, char *argv[]) exit(EXIT_FAILURE); } - ret = do_commandx(&h, argc, argv, &table); + ret = do_commandx(&h, argc, argv, &table, false); if (ret) ret = nft_commit(&h); diff --git a/iptables/xtables.c b/iptables/xtables.c index c49b4a2f..7a8ace3d 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -678,7 +678,8 @@ static void command_match(struct iptables_command_state *cs) xtables_error(OTHER_PROBLEM, "can't alloc memory!"); } -int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) +int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table, + bool restore) { struct iptables_command_state cs; int verbose = 0; @@ -1001,6 +1002,14 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) prog_name, prog_vers); exit(0); + case 'w': + if (restore) { + xtables_error(PARAMETER_PROBLEM, + "You cannot use `-w' from " + "iptables-restore"); + } + break; + case '0': set_option(&cs.options, OPT_LINENUMBERS, &args.invflags, cs.invert); -- cgit v1.2.3 From 8877968858a8dd6b7ae096988d57a7511c81733d Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Mon, 10 Feb 2014 16:49:33 +0100 Subject: nft: adds save_matches_and_target This patch permits to save matches and target for ip/ip6/arp/eb family, required for xtables-events. Also, generalizes nft_rule_print_save to be reused for all protocol families. Signed-off-by: Giuseppe Longo Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 8 +++++--- iptables/nft-ipv6.c | 8 +++++--- iptables/nft-shared.c | 34 ++++++++++++++++++++++++++++++++++ iptables/nft-shared.h | 7 +++++-- iptables/nft.c | 33 +++------------------------------ iptables/nft.h | 2 +- 6 files changed, 53 insertions(+), 39 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 3aeb8a72..02645a29 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -309,9 +309,10 @@ static void save_ipv4_addr(char letter, const struct in_addr *addr, mask_to_str(mask)); } -static uint8_t nft_ipv4_save_firewall(const struct iptables_command_state *cs, - unsigned int format) +static void nft_ipv4_save_firewall(const void *data, unsigned int format) { + const struct iptables_command_state *cs = data; + save_firewall_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto, cs->fw.ip.iniface, cs->fw.ip.iniface_mask, cs->fw.ip.outiface, cs->fw.ip.outiface_mask, @@ -328,7 +329,8 @@ static uint8_t nft_ipv4_save_firewall(const struct iptables_command_state *cs, save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr, cs->fw.ip.invflags & IPT_INV_DSTIP); - return cs->fw.ip.flags; + save_matches_and_target(cs->matches, cs->target, + cs->jumpto, cs->fw.ip.flags, &cs->fw); } static void nft_ipv4_proto_parse(struct iptables_command_state *cs, diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index f30cec61..7c8b43fc 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -218,9 +218,10 @@ static void save_ipv6_addr(char letter, const struct in6_addr *addr, printf("%s-%c %s ", invert ? "! " : "", letter, addr_str); } -static uint8_t nft_ipv6_save_firewall(const struct iptables_command_state *cs, - unsigned int format) +static void nft_ipv6_save_firewall(const void *data, unsigned int format) { + const struct iptables_command_state *cs = data; + save_firewall_details(cs, cs->fw6.ipv6.invflags, cs->fw6.ipv6.proto, cs->fw6.ipv6.iniface, cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface, cs->fw6.ipv6.outiface_mask, @@ -231,7 +232,8 @@ static uint8_t nft_ipv6_save_firewall(const struct iptables_command_state *cs, save_ipv6_addr('d', &cs->fw6.ipv6.dst, cs->fw6.ipv6.invflags & IPT_INV_DSTIP); - return cs->fw6.ipv6.flags; + save_matches_and_target(cs->matches, cs->target, + cs->jumpto, cs->fw6.ipv6.flags, &cs->fw6); } /* These are invalid numbers as upper layer protocol */ diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index d0e28ba9..dce8a34a 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -620,6 +620,40 @@ void save_firewall_details(const struct iptables_command_state *cs, } } +void save_matches_and_target(struct xtables_rule_match *m, + struct xtables_target *target, + const char *jumpto, uint8_t flags, const void *fw) +{ + struct xtables_rule_match *matchp; + + for (matchp = m; matchp; matchp = matchp->next) { + if (matchp->match->alias) { + printf("-m %s", + matchp->match->alias(matchp->match->m)); + } else + printf("-m %s", matchp->match->name); + + if (matchp->match->save != NULL) { + /* cs->fw union makes the trick */ + matchp->match->save(fw, matchp->match->m); + } + printf(" "); + } + + if (target != NULL) { + if (target->alias) { + printf("-j %s", target->alias(target->t)); + } else + printf("-j %s", jumpto); + + if (target->save != NULL) + target->save(fw, target->t); + } else if (strlen(jumpto) > 0) + printf("-%c %s", flags & IPT_F_GOTO ? 'g' : 'j', jumpto); + + printf("\n"); +} + void print_matches_and_target(struct iptables_command_state *cs, unsigned int format) { diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 1d01ebad..145f19d1 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -49,8 +49,7 @@ struct nft_family_ops { void (*parse_immediate)(const char *jumpto, bool nft_goto, void *data); void (*print_firewall)(struct nft_rule *r, unsigned int num, unsigned int format); - uint8_t (*save_firewall)(const struct iptables_command_state *cs, - unsigned int format); + void (*save_firewall)(const void *data, unsigned int format); void (*proto_parse)(struct iptables_command_state *cs, struct xtables_args *args); void (*post_parse)(int command, struct iptables_command_state *cs, @@ -118,6 +117,10 @@ void save_firewall_details(const struct iptables_command_state *cs, const char *outiface, unsigned const char *outiface_mask, unsigned int format); +void save_matches_and_target(struct xtables_rule_match *m, + struct xtables_target *target, + const char *jumpto, + uint8_t flags, const void *fw); struct nft_family_ops *nft_family_ops_lookup(int family); diff --git a/iptables/nft.c b/iptables/nft.c index fc9db998..515d124a 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1009,15 +1009,13 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table, } void -nft_rule_print_save(const struct iptables_command_state *cs, +nft_rule_print_save(const void *data, struct nft_rule *r, enum nft_rule_print type, unsigned int format) { const char *chain = nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN); int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); - struct xtables_rule_match *matchp; struct nft_family_ops *ops; - int ip_flags = 0; /* print chain name */ switch(type) { @@ -1030,35 +1028,10 @@ nft_rule_print_save(const struct iptables_command_state *cs, } ops = nft_family_ops_lookup(family); - ip_flags = ops->save_firewall(cs, format); - - for (matchp = cs->matches; matchp; matchp = matchp->next) { - if (matchp->match->alias) { - printf("-m %s", - matchp->match->alias(matchp->match->m)); - } else - printf("-m %s", matchp->match->name); - - if (matchp->match->save != NULL) { - /* cs->fw union makes the trick */ - matchp->match->save(&cs->fw, matchp->match->m); - } - printf(" "); - } - if (cs->target != NULL) { - if (cs->target->alias) { - printf("-j %s", cs->target->alias(cs->target->t)); - } else - printf("-j %s", cs->jumpto); + if (ops->save_firewall) + ops->save_firewall(data, format); - if (cs->target->save != NULL) - cs->target->save(&cs->fw, cs->target->t); - } else if (strlen(cs->jumpto) > 0) - printf("-%c %s", ip_flags & IPT_F_GOTO ? 'g' : 'j', - cs->jumpto); - - printf("\n"); } static int nft_chain_list_cb(const struct nlmsghdr *nlh, void *data) diff --git a/iptables/nft.h b/iptables/nft.h index 22af66e2..8670f343 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -114,7 +114,7 @@ enum nft_rule_print { NFT_RULE_DEL, }; -void nft_rule_print_save(const struct iptables_command_state *cs, +void nft_rule_print_save(const void *data, struct nft_rule *r, enum nft_rule_print type, unsigned int format); -- cgit v1.2.3 From d007e1a59e4beaddab430992302d43b122ffc801 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 11 Feb 2014 13:58:03 +0100 Subject: nft-compat: fix IP6T_F_GOTO flag handling IPT_F_GOTO and IP6T_F_GOTO don't overlap, so this need special handling to avoid misinterpretations. Signed-off-by: Pablo Neira Ayuso --- iptables/nft-ipv4.c | 8 +++++++- iptables/nft-ipv6.c | 14 +++++++++----- iptables/nft-shared.c | 5 +---- iptables/nft.c | 4 ++-- iptables/nft.h | 2 +- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 02645a29..d05e80eb 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -76,7 +76,7 @@ static int nft_ipv4_add(struct nft_rule *r, void *data) if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) return -1; - return add_action(r, cs, cs->fw.ip.flags); + return add_action(r, cs, !!(cs->fw.ip.flags & IPT_F_GOTO)); } static bool nft_ipv4_is_same(const void *data_a, @@ -331,6 +331,12 @@ static void nft_ipv4_save_firewall(const void *data, unsigned int format) save_matches_and_target(cs->matches, cs->target, cs->jumpto, cs->fw.ip.flags, &cs->fw); + + if (cs->target == NULL && strlen(cs->jumpto) > 0) { + printf("-%c %s", cs->fw.ip.flags & IPT_F_GOTO ? 'g' : 'j', + cs->jumpto); + } + printf("\n"); } static void nft_ipv4_proto_parse(struct iptables_command_state *cs, diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 7c8b43fc..f08598ae 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -59,7 +59,7 @@ static int nft_ipv6_add(struct nft_rule *r, void *data) if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) return -1; - return add_action(r, cs, cs->fw6.ipv6.flags); + return add_action(r, cs, !!(cs->fw6.ipv6.flags & IP6T_F_GOTO)); } static bool nft_ipv6_is_same(const void *data_a, @@ -138,7 +138,7 @@ static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto, cs->jumpto = jumpto; if (nft_goto) - cs->fw6.ipv6.flags |= IPT_F_GOTO; + cs->fw6.ipv6.flags |= IP6T_F_GOTO; } static void print_ipv6_addr(const struct iptables_command_state *cs, @@ -195,10 +195,8 @@ static void nft_ipv6_print_firewall(struct nft_rule *r, unsigned int num, if (format & FMT_NOTABLE) fputs(" ", stdout); -#ifdef IPT_F_GOTO - if (cs.fw6.ipv6.flags & IPT_F_GOTO) + if (cs.fw6.ipv6.flags & IP6T_F_GOTO) printf("[goto] "); -#endif print_matches_and_target(&cs, format); @@ -234,6 +232,12 @@ static void nft_ipv6_save_firewall(const void *data, unsigned int format) save_matches_and_target(cs->matches, cs->target, cs->jumpto, cs->fw6.ipv6.flags, &cs->fw6); + + if (cs->target == NULL && strlen(cs->jumpto) > 0) { + printf("-%c %s", cs->fw6.ipv6.flags & IP6T_F_GOTO ? 'g' : 'j', + cs->jumpto); + } + printf("\n"); } /* These are invalid numbers as upper layer protocol */ diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index dce8a34a..ada71e6b 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -648,10 +648,7 @@ void save_matches_and_target(struct xtables_rule_match *m, if (target->save != NULL) target->save(fw, target->t); - } else if (strlen(jumpto) > 0) - printf("-%c %s", flags & IPT_F_GOTO ? 'g' : 'j', jumpto); - - printf("\n"); + } } void print_matches_and_target(struct iptables_command_state *cs, diff --git a/iptables/nft.c b/iptables/nft.c index 515d124a..a45d599b 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -864,7 +864,7 @@ int add_verdict(struct nft_rule *r, int verdict) } int add_action(struct nft_rule *r, struct iptables_command_state *cs, - int ip_flags) + bool goto_set) { int ret = 0; @@ -881,7 +881,7 @@ int add_action(struct nft_rule *r, struct iptables_command_state *cs, ret = add_target(r, cs->target->t); } else if (strlen(cs->jumpto) > 0) { /* Not standard, then it's a go / jump to chain */ - if (ip_flags & IPT_F_GOTO) + if (goto_set) ret = add_jumpto(r, cs->jumpto, NFT_GOTO); else ret = add_jumpto(r, cs->jumpto, NFT_JUMP); diff --git a/iptables/nft.h b/iptables/nft.h index 8670f343..92488764 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -107,7 +107,7 @@ int add_verdict(struct nft_rule *r, int verdict); int add_match(struct nft_rule *r, struct xt_entry_match *m); int add_target(struct nft_rule *r, struct xt_entry_target *t); int add_jumpto(struct nft_rule *r, const char *name, int verdict); -int add_action(struct nft_rule *r, struct iptables_command_state *cs, int ip_flags); +int add_action(struct nft_rule *r, struct iptables_command_state *cs, bool goto_set); enum nft_rule_print { NFT_RULE_APPEND, -- cgit v1.2.3 From 5cab9c3c8209e9491f0f252e03dd48ae4cb5ab63 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 11 Feb 2014 14:24:06 +0100 Subject: nft-compat: fix wrong protocol context in initialization This fixes matches/targets that are dependent on that IPv4/Ipv6 context, eg. # ip6tables-compat -I INPUT -j REJECT --reject-with icmp6-addr-unreachable # ip6tables-compat-save ... -A INPUT -j REJECT --reject-with icmp6-port-unreachable Signed-off-by: Pablo Neira Ayuso --- iptables/xtables-save.c | 3 +-- iptables/xtables.c | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 93065cfc..42d29071 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -85,8 +85,7 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[]) int c; xtables_globals.program_name = progname; - /* XXX xtables_init_all does several things we don't want */ - c = xtables_init_all(&xtables_globals, NFPROTO_IPV4); + c = xtables_init_all(&xtables_globals, family); if (c < 0) { fprintf(stderr, "%s/%s Failed to initialize xtables\n", xtables_globals.program_name, diff --git a/iptables/xtables.c b/iptables/xtables.c index 7a8ace3d..45a5ac63 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -691,7 +691,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table, struct xtables_rule_match *matchp; struct xtables_target *t; struct xtables_args args = { - .family = AF_INET, + .family = h->family, }; memset(&cs, 0, sizeof(cs)); @@ -716,8 +716,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table, demand-load a protocol. */ opterr = 0; - /* Default on AF_INET */ - h->ops = nft_family_ops_lookup(AF_INET); + h->ops = nft_family_ops_lookup(h->family); if (h->ops == NULL) xtables_error(PARAMETER_PROBLEM, "Unknown family"); -- cgit v1.2.3 From 837629fed24af7298fbf4cd28c7a51f24b70ee93 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 11 Feb 2014 18:36:42 +0200 Subject: nft: Add useful debug output when a builtin table is created This is useful to know if a builtin table is requested to be created. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index a45d599b..36ac69cc 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -451,6 +451,14 @@ nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, nft_table_nlmsg_build_payload(nlh, t); nft_table_free(t); +#ifdef NLDEBUG + char tmp[1024]; + + nft_table_snprintf(tmp, sizeof(tmp), t, 0, 0); + printf("DEBUG: table: %s", tmp); + mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg)); +#endif + ret = mnl_talk(h, nlh, NULL, NULL); if (ret < 0) { if (errno != EEXIST) -- cgit v1.2.3 From 690ea18fdd6f8bc12322a729a2f7c97d8e731c43 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 11 Feb 2014 18:36:43 +0200 Subject: nft: A builtin chain might be created when restoring nft_chain_set() is directly used in xtables-restore.c, however at that point no builtin chains have been created yet thus the need to request to build it relevantly. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 1 + iptables/nft.h | 1 + iptables/xtables-restore.c | 1 + 3 files changed, 3 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index 36ac69cc..501c6d86 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -744,6 +744,7 @@ __nft_chain_set(struct nft_handle *h, const char *table, } nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, + h->restore ? NLM_F_ACK|NLM_F_CREATE : NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); diff --git a/iptables/nft.h b/iptables/nft.h index 92488764..3b58d514 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -34,6 +34,7 @@ struct nft_handle { struct mnl_nlmsg_batch *batch; struct nft_family_ops *ops; struct builtin_table *tables; + bool restore; }; extern struct builtin_table xtables_ipv4[TABLES_MAX]; diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 730800ff..f7850bb2 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -170,6 +170,7 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[]) { struct nft_handle h = { .family = family, + .restore = true, }; char buffer[10240]; int c; -- cgit v1.2.3 From e6b8e172fca48f5d80699afe80947b0fc1f23fd6 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 11 Feb 2014 18:36:44 +0200 Subject: nft: Initialize a table only once This helps to remove some runtime overhead, especially when running xtables-restore. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 10 ++++++++++ iptables/nft.h | 1 + 2 files changed, 11 insertions(+) diff --git a/iptables/nft.c b/iptables/nft.c index 501c6d86..49322bdd 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -436,6 +436,9 @@ nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, struct nft_table *t; int ret; + if (_t->initialized) + return 0; + t = nft_table_alloc(); if (t == NULL) return -1; @@ -464,6 +467,10 @@ nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, if (errno != EEXIST) perror("mnl-talk:nft_table_init_one"); } + + if (ret == 0 || errno == EEXIST) + _t->initialized = true; + return ret; } @@ -2414,6 +2421,9 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename, uint32_t table_family, chain_family; bool found = false; + if (h->restore) + return 0; + if (xtables_config_parse(filename, table_list, chain_list) < 0) { if (errno == ENOENT) { xtables_config_perror(flags, diff --git a/iptables/nft.h b/iptables/nft.h index 3b58d514..c31371c0 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -22,6 +22,7 @@ struct builtin_chain { struct builtin_table { const char *name; struct builtin_chain chains[NF_INET_NUMHOOKS]; + bool initialized; }; struct nft_handle { -- cgit v1.2.3 From 2e256aa818ba5ddf13a4e85f071ef1bf3c485558 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 11 Feb 2014 18:36:45 +0200 Subject: nft: Remove useless error message These are not helpful. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 45 +++++---------------------------------------- 1 file changed, 5 insertions(+), 40 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 49322bdd..d3b9bf01 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -463,11 +463,6 @@ nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, #endif ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) { - if (errno != EEXIST) - perror("mnl-talk:nft_table_init_one"); - } - if (ret == 0 || errno == EEXIST) _t->initialized = true; @@ -513,10 +508,7 @@ nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, nft_chain_nlmsg_build_payload(nlh, c); nft_chain_free(c); - if (mnl_talk(h, nlh, NULL, NULL) < 0) { - if (errno != EEXIST) - perror("mnl_talk:nft_chain_builtin_add"); - } + mnl_talk(h, nlh, NULL, NULL); } /* find if built-in table already exists */ @@ -725,7 +717,6 @@ __nft_chain_set(struct nft_handle *h, const char *table, struct nft_chain *c; struct builtin_table *_t; struct builtin_chain *_c; - int ret; _t = nft_table_builtin_find(h, table); /* if this built-in table does not exists, create it */ @@ -759,11 +750,7 @@ __nft_chain_set(struct nft_handle *h, const char *table, nft_chain_free(c); - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) - perror("mnl_talk:__nft_chain_policy"); - - return ret; + return mnl_talk(h, nlh, NULL, NULL); } int nft_chain_set(struct nft_handle *h, const char *table, @@ -1079,7 +1066,6 @@ static struct nft_chain_list *nft_chain_list_get(struct nft_handle *h) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; - int ret; struct nft_chain_list *list; list = nft_chain_list_alloc(); @@ -1091,9 +1077,7 @@ static struct nft_chain_list *nft_chain_list_get(struct nft_handle *h) nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family, NLM_F_DUMP, h->seq); - ret = mnl_talk(h, nlh, nft_chain_list_cb, list); - if (ret < 0) - perror("mnl_talk:nft_chain_list_get"); + mnl_talk(h, nlh, nft_chain_list_cb, list); return list; } @@ -1201,7 +1185,6 @@ static struct nft_rule_list *nft_rule_list_get(struct nft_handle *h) ret = mnl_talk(h, nlh, nft_rule_list_cb, list); if (ret < 0) { - perror("mnl_talk:nft_rule_save"); nft_rule_list_free(list); return NULL; } @@ -1336,10 +1319,6 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl nft_chain_free(c); ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) { - if (errno != EEXIST) - perror("mnl_talk:nft_chain_add"); - } /* the core expects 1 for success and 0 for error */ return ret == 0 ? 1 : 0; @@ -1349,19 +1328,12 @@ static int __nft_chain_del(struct nft_handle *h, struct nft_chain *c) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; - int ret; nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, h->family, NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) { - if (errno != EEXIST && errno != ENOENT) - perror("mnl_talk:__nft_chain_del"); - } - - return ret; + return mnl_talk(h, nlh, NULL, NULL); } int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table) @@ -1502,10 +1474,6 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, nft_chain_free(c); ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) { - if (errno != EEXIST) - perror("mnl_talk:nft_chain_rename"); - } /* the core expects 1 for success and 0 for error */ return ret == 0 ? 1 : 0; @@ -1540,7 +1508,6 @@ static struct nft_table_list *nft_table_list_get(struct nft_handle *h) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; - int ret; struct nft_table_list *list; list = nft_table_list_alloc(); @@ -1550,9 +1517,7 @@ static struct nft_table_list *nft_table_list_get(struct nft_handle *h) nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family, NLM_F_DUMP, h->seq); - ret = mnl_talk(h, nlh, nft_table_list_cb, list); - if (ret < 0) - perror("mnl_talk:nft_table_list_get"); + mnl_talk(h, nlh, nft_table_list_cb, list); return list; } -- cgit v1.2.3 From 1835790d7f7517f4c101e1c1f3df5519a6c228e7 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 11 Feb 2014 18:36:46 +0200 Subject: nft: Pass a line after printing out a debug message In this specific places, libnftnl gives back a string on which iptables should not assume any line break, thus it's up to iptables to add it. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index d3b9bf01..1237659f 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -458,7 +458,7 @@ nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, char tmp[1024]; nft_table_snprintf(tmp, sizeof(tmp), t, 0, 0); - printf("DEBUG: table: %s", tmp); + printf("DEBUG: table: %s\n", tmp); mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg)); #endif @@ -702,7 +702,7 @@ static void nft_chain_print_debug(struct nft_chain *c, struct nlmsghdr *nlh) char tmp[1024]; nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0); - printf("DEBUG: chain: %s", tmp); + printf("DEBUG: chain: %s\n", tmp); mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg)); #endif } @@ -898,7 +898,7 @@ static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh) char tmp[1024]; nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0); - printf("DEBUG: rule: %s", tmp); + printf("DEBUG: rule: %s\n", tmp); mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg)); #endif } -- cgit v1.2.3