aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2017-03-24 06:32:41 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-03-24 06:32:41 +0000
commite20d6bf62b07e455e15b3d8982fb83dc99b9e564 (patch)
tree4400fe0ba9baae676633293796954b4061726d5d
parenta1ffd5ecfa5d72c6dc4cfaf11653d61e3e9083bc (diff)
parentff45753ae3c3108c6c93ec132f7cf62190f9c628 (diff)
downloadplatform_external_iptables-e20d6bf62b07e455e15b3d8982fb83dc99b9e564.tar.gz
platform_external_iptables-e20d6bf62b07e455e15b3d8982fb83dc99b9e564.tar.bz2
platform_external_iptables-e20d6bf62b07e455e15b3d8982fb83dc99b9e564.zip
Merge changes from topic 'iptables-1.6.1' am: c784fc47e6
am: ff45753ae3 Change-Id: Ic463667ae6ac346f8eae4b6ca18888dcd24b9d6d
-rw-r--r--Android.mk2
-rw-r--r--Makefile.am7
-rw-r--r--README.version6
-rw-r--r--config.h11
-rw-r--r--configure.ac131
-rw-r--r--etc/ethertypes39
-rw-r--r--etc/xtables.conf75
-rw-r--r--extensions/Android.mk5
-rw-r--r--extensions/GNUmakefile.in85
-rw-r--r--extensions/libarpt_mangle.c204
-rw-r--r--extensions/libebt_802_3.c133
-rw-r--r--extensions/libebt_ip.c312
-rw-r--r--extensions/libebt_limit.c179
-rw-r--r--extensions/libebt_log.c197
-rw-r--r--extensions/libebt_mark.c191
-rw-r--r--extensions/libebt_mark_m.c118
-rw-r--r--extensions/libebt_nflog.c144
-rw-r--r--extensions/libip6t_DNAT.c51
-rw-r--r--extensions/libip6t_DNAT.t8
-rw-r--r--extensions/libip6t_DNPT.c8
-rw-r--r--extensions/libip6t_DNPT.t7
-rw-r--r--extensions/libip6t_HL.t10
-rw-r--r--extensions/libip6t_LOG.c64
-rw-r--r--extensions/libip6t_LOG.t12
-rw-r--r--extensions/libip6t_MASQUERADE.c21
-rw-r--r--extensions/libip6t_MASQUERADE.t8
-rw-r--r--extensions/libip6t_NETMAP.c13
-rw-r--r--extensions/libip6t_NETMAP.t4
-rw-r--r--extensions/libip6t_REDIRECT.c19
-rw-r--r--extensions/libip6t_REDIRECT.t6
-rw-r--r--extensions/libip6t_REJECT.c44
-rw-r--r--extensions/libip6t_REJECT.man7
-rw-r--r--extensions/libip6t_REJECT.t11
-rw-r--r--extensions/libip6t_SNAT.c75
-rw-r--r--extensions/libip6t_SNAT.t8
-rw-r--r--extensions/libip6t_SNPT.c8
-rw-r--r--extensions/libip6t_SNPT.t7
-rw-r--r--extensions/libip6t_ah.c45
-rw-r--r--extensions/libip6t_ah.t15
-rw-r--r--extensions/libip6t_dst.c2
-rw-r--r--extensions/libip6t_dst.t5
-rw-r--r--extensions/libip6t_eui64.t8
-rw-r--r--extensions/libip6t_frag.c40
-rw-r--r--extensions/libip6t_frag.t11
-rw-r--r--extensions/libip6t_hbh.c18
-rw-r--r--extensions/libip6t_hbh.t5
-rw-r--r--extensions/libip6t_hl.c19
-rw-r--r--extensions/libip6t_hl.t8
-rw-r--r--extensions/libip6t_icmp6.c69
-rw-r--r--extensions/libip6t_icmp6.t6
-rw-r--r--extensions/libip6t_ipv6header.c2
-rw-r--r--extensions/libip6t_ipv6header.man4
-rw-r--r--extensions/libip6t_ipv6header.t4
-rw-r--r--extensions/libip6t_mh.c21
-rw-r--r--extensions/libip6t_mh.t6
-rw-r--r--extensions/libip6t_rt.c46
-rw-r--r--extensions/libip6t_rt.t5
-rw-r--r--extensions/libipt_DNAT.c46
-rw-r--r--extensions/libipt_DNAT.t8
-rw-r--r--extensions/libipt_ECN.t5
-rw-r--r--extensions/libipt_LOG.c64
-rw-r--r--extensions/libipt_LOG.t12
-rw-r--r--extensions/libipt_MASQUERADE.c23
-rw-r--r--extensions/libipt_MASQUERADE.t8
-rw-r--r--extensions/libipt_MIRROR.c15
-rw-r--r--extensions/libipt_MIRROR.man12
-rw-r--r--extensions/libipt_NETMAP.c13
-rw-r--r--extensions/libipt_NETMAP.t4
-rw-r--r--extensions/libipt_REDIRECT.c19
-rw-r--r--extensions/libipt_REDIRECT.t6
-rw-r--r--extensions/libipt_REJECT.c49
-rw-r--r--extensions/libipt_REJECT.man8
-rw-r--r--extensions/libipt_REJECT.t9
-rw-r--r--extensions/libipt_SAME.c186
-rw-r--r--extensions/libipt_SAME.man17
-rw-r--r--extensions/libipt_SNAT.c71
-rw-r--r--extensions/libipt_SNAT.t8
-rw-r--r--extensions/libipt_TTL.t10
-rw-r--r--extensions/libipt_ULOG.t19
-rw-r--r--extensions/libipt_ah.c39
-rw-r--r--extensions/libipt_ah.t13
-rw-r--r--extensions/libipt_icmp.c36
-rw-r--r--extensions/libipt_icmp.t15
-rw-r--r--extensions/libipt_realm.c42
-rw-r--r--extensions/libipt_realm.t4
-rw-r--r--extensions/libipt_ttl.c30
-rw-r--r--extensions/libipt_ttl.t15
-rw-r--r--extensions/libipt_unclean.c15
-rw-r--r--extensions/libipt_unclean.man2
-rw-r--r--extensions/libxt_AUDIT.t6
-rw-r--r--extensions/libxt_CHECKSUM.t4
-rw-r--r--extensions/libxt_CLASSIFY.c71
-rw-r--r--extensions/libxt_CLASSIFY.t9
-rw-r--r--extensions/libxt_CONNMARK.c47
-rw-r--r--extensions/libxt_CONNMARK.t7
-rw-r--r--extensions/libxt_CONNSECMARK.t5
-rw-r--r--extensions/libxt_CT.c87
-rw-r--r--extensions/libxt_CT.man16
-rw-r--r--extensions/libxt_CT.t20
-rw-r--r--extensions/libxt_DSCP.c64
-rw-r--r--extensions/libxt_DSCP.t11
-rw-r--r--extensions/libxt_HMARK.t8
-rw-r--r--extensions/libxt_IDLETIMER.t4
-rw-r--r--extensions/libxt_LED.t4
-rw-r--r--extensions/libxt_LOG.man6
-rw-r--r--extensions/libxt_MARK.c51
-rw-r--r--extensions/libxt_MARK.t7
-rw-r--r--extensions/libxt_NFLOG.c59
-rw-r--r--extensions/libxt_NFLOG.man3
-rw-r--r--extensions/libxt_NFLOG.t24
-rw-r--r--extensions/libxt_NFQUEUE.c158
-rw-r--r--extensions/libxt_NFQUEUE.man13
-rw-r--r--extensions/libxt_NFQUEUE.t16
-rw-r--r--extensions/libxt_NOTRACK.t4
-rw-r--r--extensions/libxt_RATEEST.t2
-rw-r--r--extensions/libxt_SET.c193
-rw-r--r--extensions/libxt_SET.man21
-rw-r--r--extensions/libxt_SET.t3
-rw-r--r--extensions/libxt_SNAT.man7
-rw-r--r--extensions/libxt_SYNPROXY.c127
-rw-r--r--extensions/libxt_SYNPROXY.man64
-rw-r--r--extensions/libxt_SYNPROXY.t3
-rw-r--r--extensions/libxt_TCPMSS.t6
-rw-r--r--extensions/libxt_TCPOPTSTRIP.c15
-rw-r--r--extensions/libxt_TCPOPTSTRIP.t8
-rw-r--r--extensions/libxt_TEE.c36
-rw-r--r--extensions/libxt_TEE.t4
-rw-r--r--extensions/libxt_TOS.t16
-rw-r--r--extensions/libxt_TPROXY.t5
-rw-r--r--extensions/libxt_TRACE.c8
-rw-r--r--extensions/libxt_TRACE.t3
-rw-r--r--extensions/libxt_addrtype.t17
-rw-r--r--extensions/libxt_bpf.c220
-rw-r--r--extensions/libxt_bpf.man32
-rw-r--r--extensions/libxt_bpf.t2
-rw-r--r--extensions/libxt_cgroup.c184
-rw-r--r--extensions/libxt_cgroup.man30
-rw-r--r--extensions/libxt_cgroup.t8
-rw-r--r--extensions/libxt_cluster.man5
-rw-r--r--extensions/libxt_cluster.t10
-rw-r--r--extensions/libxt_comment.c21
-rw-r--r--extensions/libxt_comment.t12
-rw-r--r--extensions/libxt_connbytes.t21
-rw-r--r--extensions/libxt_connlabel.c59
-rw-r--r--extensions/libxt_connlabel.t18
-rw-r--r--extensions/libxt_connlimit.t16
-rw-r--r--extensions/libxt_connmark.c50
-rw-r--r--extensions/libxt_connmark.t9
-rw-r--r--extensions/libxt_conntrack.c243
-rw-r--r--extensions/libxt_conntrack.man4
-rw-r--r--extensions/libxt_conntrack.t27
-rw-r--r--extensions/libxt_cpu.c13
-rw-r--r--extensions/libxt_cpu.t6
-rw-r--r--extensions/libxt_dccp.c92
-rw-r--r--extensions/libxt_dccp.t30
-rw-r--r--extensions/libxt_devgroup.c62
-rw-r--r--extensions/libxt_dscp.c71
-rw-r--r--extensions/libxt_dscp.t10
-rw-r--r--extensions/libxt_ecn.c31
-rw-r--r--extensions/libxt_ecn.t5
-rw-r--r--extensions/libxt_esp.c31
-rw-r--r--extensions/libxt_esp.t8
-rw-r--r--extensions/libxt_hashlimit.c487
-rw-r--r--extensions/libxt_hashlimit.man2
-rw-r--r--extensions/libxt_hashlimit.t28
-rw-r--r--extensions/libxt_helper.c16
-rw-r--r--extensions/libxt_helper.t6
-rw-r--r--extensions/libxt_ipcomp.c134
-rw-r--r--extensions/libxt_ipcomp.c.man7
-rw-r--r--extensions/libxt_iprange.c113
-rw-r--r--extensions/libxt_iprange.t11
-rw-r--r--extensions/libxt_length.c16
-rw-r--r--extensions/libxt_length.t10
-rw-r--r--extensions/libxt_limit.c39
-rw-r--r--extensions/libxt_limit.t6
-rw-r--r--extensions/libxt_mac.c27
-rw-r--r--extensions/libxt_mac.t5
-rw-r--r--extensions/libxt_mangle.c396
-rw-r--r--extensions/libxt_mark.c48
-rw-r--r--extensions/libxt_mark.t7
-rw-r--r--extensions/libxt_multiport.c110
-rw-r--r--extensions/libxt_multiport.t23
-rw-r--r--extensions/libxt_nfacct.t10
-rw-r--r--extensions/libxt_osf.c2
-rw-r--r--extensions/libxt_osf.t4
-rw-r--r--extensions/libxt_owner.c51
-rw-r--r--extensions/libxt_owner.t12
-rw-r--r--extensions/libxt_physdev.man14
-rw-r--r--extensions/libxt_physdev.t14
-rw-r--r--extensions/libxt_pkttype.c37
-rw-r--r--extensions/libxt_pkttype.t6
-rw-r--r--extensions/libxt_policy.t5
-rw-r--r--extensions/libxt_quota.c14
-rw-r--r--extensions/libxt_quota.t7
-rw-r--r--extensions/libxt_rateest.t16
-rw-r--r--extensions/libxt_recent.c2
-rw-r--r--extensions/libxt_recent.man3
-rw-r--r--extensions/libxt_recent.t11
-rw-r--r--extensions/libxt_rpfilter.man2
-rw-r--r--extensions/libxt_rpfilter.t4
-rw-r--r--extensions/libxt_sctp.c39
-rw-r--r--extensions/libxt_sctp.t32
-rw-r--r--extensions/libxt_set.c200
-rw-r--r--extensions/libxt_set.h52
-rw-r--r--extensions/libxt_set.man2
-rw-r--r--extensions/libxt_set.t4
-rw-r--r--extensions/libxt_socket.man35
-rw-r--r--extensions/libxt_socket.t8
-rw-r--r--extensions/libxt_standard.t4
-rw-r--r--extensions/libxt_state.t6
-rw-r--r--extensions/libxt_statistic.c21
-rw-r--r--extensions/libxt_statistic.t8
-rw-r--r--extensions/libxt_string.man13
-rw-r--r--extensions/libxt_string.t18
-rw-r--r--extensions/libxt_tcp.c84
-rw-r--r--extensions/libxt_tcp.man1
-rw-r--r--extensions/libxt_tcp.t26
-rw-r--r--extensions/libxt_tcpmss.t5
-rw-r--r--extensions/libxt_time.t4
-rw-r--r--extensions/libxt_tos.t13
-rw-r--r--extensions/libxt_u32.t2
-rw-r--r--extensions/libxt_udp.c39
-rw-r--r--extensions/libxt_udp.t22
-rw-r--r--include/ebtables/ethernetdb.h57
-rw-r--r--include/iptables.h2
-rw-r--r--include/iptables/internal.h2
-rw-r--r--include/libiptc/ipt_kernel_headers.h12
-rw-r--r--include/linux/filter.h139
-rw-r--r--include/linux/netfilter/ipset/ip_set.h27
-rw-r--r--include/linux/netfilter/nf_nat.h12
-rw-r--r--include/linux/netfilter/nf_tables.h793
-rw-r--r--include/linux/netfilter/nf_tables_compat.h20
-rw-r--r--include/linux/netfilter/nfnetlink.h64
-rw-r--r--include/linux/netfilter/xt_CT.h3
-rw-r--r--include/linux/netfilter/xt_NFLOG.h6
-rw-r--r--include/linux/netfilter/xt_SYNPROXY.h16
-rw-r--r--include/linux/netfilter/xt_bpf.h25
-rw-r--r--include/linux/netfilter/xt_cgroup.h24
-rw-r--r--include/linux/netfilter/xt_hashlimit.h26
-rw-r--r--include/linux/netfilter/xt_ipcomp.h16
-rw-r--r--include/linux/netfilter/xt_osf.h2
-rw-r--r--include/linux/netfilter/xt_set.h19
-rw-r--r--include/linux/netfilter_arp.h19
-rw-r--r--include/linux/netfilter_arp/arp_tables.h204
-rw-r--r--include/linux/netfilter_arp/arpt_mangle.h26
-rw-r--r--include/linux/netfilter_bridge.h33
-rw-r--r--include/linux/netfilter_bridge/ebt_802_3.h63
-rw-r--r--include/linux/netfilter_bridge/ebt_ip.h44
-rw-r--r--include/linux/netfilter_bridge/ebt_mark_m.h16
-rw-r--r--include/linux/netfilter_bridge/ebt_mark_t.h23
-rw-r--r--include/linux/netfilter_ipv4/ip_tables.h16
-rw-r--r--include/linux/netfilter_ipv4/ipt_SAME.h20
-rw-r--r--include/linux/netfilter_ipv6/ip6_tables.h21
-rw-r--r--include/linux/netfilter_ipv6/ip6t_REJECT.h4
-rw-r--r--include/xtables.h76
-rwxr-xr-xiptables-test.py311
-rw-r--r--iptables/.gitignore7
-rw-r--r--iptables/Android.mk19
-rw-r--r--iptables/Makefile.am44
-rw-r--r--iptables/getethertype.c161
-rw-r--r--iptables/ip6tables-restore.869
-rw-r--r--iptables/ip6tables-restore.c50
-rw-r--r--iptables/ip6tables-save.854
-rw-r--r--iptables/ip6tables-save.c11
-rw-r--r--iptables/ip6tables-standalone.c2
-rw-r--r--iptables/ip6tables.81
-rw-r--r--iptables/ip6tables.8.in463
-rw-r--r--iptables/ip6tables.c165
-rw-r--r--iptables/iptables-apply.8.in (renamed from iptables/iptables-apply.8)2
-rw-r--r--iptables/iptables-restore.8.in (renamed from iptables/iptables-restore.8)30
-rw-r--r--iptables/iptables-restore.c49
-rw-r--r--iptables/iptables-save.8.in (renamed from iptables/iptables-save.8)19
-rw-r--r--iptables/iptables-save.c11
-rw-r--r--iptables/iptables-xml.1.in (renamed from iptables/iptables-xml.1)2
-rw-r--r--iptables/iptables-xml.c25
-rw-r--r--iptables/iptables.8.in88
-rw-r--r--iptables/iptables.c159
-rw-r--r--iptables/iptables.xslt2
-rw-r--r--iptables/nft-arp.c672
-rw-r--r--iptables/nft-arp.h16
-rw-r--r--iptables/nft-bridge.c647
-rw-r--r--iptables/nft-bridge.h171
-rw-r--r--iptables/nft-ipv4.c517
-rw-r--r--iptables/nft-ipv6.c466
-rw-r--r--iptables/nft-shared.c883
-rw-r--r--iptables/nft-shared.h272
-rw-r--r--iptables/nft.c2906
-rw-r--r--iptables/nft.h187
-rw-r--r--iptables/xshared.c111
-rw-r--r--iptables/xshared.h16
-rw-r--r--iptables/xtables-arp-standalone.c77
-rw-r--r--iptables/xtables-arp.c1480
-rw-r--r--iptables/xtables-compat-multi.c42
-rw-r--r--iptables/xtables-config-parser.y248
-rw-r--r--iptables/xtables-config-syntax.l54
-rw-r--r--iptables/xtables-eb-standalone.c74
-rw-r--r--iptables/xtables-eb.c1379
-rw-r--r--iptables/xtables-multi.c13
-rw-r--r--iptables/xtables-multi.h16
-rw-r--r--iptables/xtables-restore.c539
-rw-r--r--iptables/xtables-save.c162
-rw-r--r--iptables/xtables-standalone.c104
-rw-r--r--iptables/xtables-translate.c522
-rw-r--r--iptables/xtables.c1299
-rw-r--r--iptables/xtables.lock0
-rw-r--r--libiptc/libiptc.c8
-rw-r--r--libiptc/linux_list.h2
-rw-r--r--libxtables/xtables.c208
-rw-r--r--libxtables/xtoptions.c8
-rw-r--r--tests/options-ipv4.rules52
-rw-r--r--tests/options-most.rules214
-rw-r--r--utils/Makefile.am5
-rw-r--r--utils/nfnl_osf.c2
-rw-r--r--utils/nfsynproxy.c228
-rw-r--r--utils/pf.os37
315 files changed, 23599 insertions, 1919 deletions
diff --git a/Android.mk b/Android.mk
index 31eae917..4a2bd29f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,5 +1,3 @@
-BUILD_IPTABLES_V14 := 1
-
LOCAL_PATH:= $(call my-dir)
include $(call all-subdir-makefiles)
diff --git a/Makefile.am b/Makefile.am
index c38d3600..044f6461 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,12 +16,17 @@ SUBDIRS += extensions
# Depends on extensions/libext.a:
SUBDIRS += iptables
+if ENABLE_NFTABLES
+confdir = $(sysconfdir)
+dist_conf_DATA = etc/ethertypes
+endif
+
.PHONY: tarball
tarball:
rm -Rf /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION};
pushd ${top_srcdir} && git archive --prefix=${PACKAGE_TARNAME}-${PACKAGE_VERSION}/ HEAD | tar -C /tmp -x && popd;
pushd /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION} && ./autogen.sh && popd;
- tar -C /tmp -cjf ${PACKAGE_TARNAME}-${PACKAGE_VERSION}.tar.bz2 --owner=root --group=root ${PACKAGE_TARNAME}-${PACKAGE_VERSION}/;
+ tar --exclude=*.t --exclude=iptables-test.py -C /tmp -cjf ${PACKAGE_TARNAME}-${PACKAGE_VERSION}.tar.bz2 --owner=root --group=root ${PACKAGE_TARNAME}-${PACKAGE_VERSION}/;
rm -Rf /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION};
config.status: extensions/GNUmakefile.in \
diff --git a/README.version b/README.version
index 84584bd5..74c158a5 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,3 @@
-URL: ftp://ftp.netfilter.org/pub/iptables/iptables-1.4.20.tar.bz2
-Version: 1.4.20
-BugComponent: 24950
+URL: git://git.netfilter.org/iptables
+Version: 1.6.1
+BugComponent: 31808
diff --git a/config.h b/config.h
index 1b4079d7..45a1adf2 100644
--- a/config.h
+++ b/config.h
@@ -20,7 +20,7 @@
#define HAVE_LINUX_MAGIC_H 1
/* Define to 1 if you have the <linux/proc_fs.h> header file. */
-/* #undef HAVE_LINUX_PROC_FS_H */
+#define HAVE_LINUX_PROC_FS_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
@@ -63,7 +63,7 @@
#define PACKAGE_NAME "iptables"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "iptables 1.4.20"
+#define PACKAGE_STRING "iptables 1.6.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "iptables"
@@ -72,7 +72,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.4.20"
+#define PACKAGE_VERSION "1.6.1"
/* The size of `struct ip6_hdr', as computed by sizeof. */
#define SIZEOF_STRUCT_IP6_HDR 40
@@ -81,4 +81,7 @@
#define STDC_HEADERS 1
/* Version number of package */
-#define VERSION "1.4.20"
+#define VERSION "1.6.1"
+
+/* Location of the iptables lock file */
+#define XT_LOCK_NAME "/system/etc/xtables.lock"
diff --git a/configure.ac b/configure.ac
index f8affedd..221812a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,8 +1,8 @@
-AC_INIT([iptables], [1.4.20])
+AC_INIT([iptables], [1.6.1])
# See libtool.info "Libtool's versioning system"
-libxtables_vcurrent=10
+libxtables_vcurrent=12
libxtables_vage=0
AC_CONFIG_AUX_DIR([build-aux])
@@ -53,10 +53,24 @@ AC_ARG_ENABLE([libipq],
[enable_libipq="$enableval"], [enable_libipq="no"])
AC_ARG_ENABLE([bpf-compiler],
AS_HELP_STRING([--enable-bpf-compiler], [Build bpf compiler]),
- [enable_bpfc="yes"], [enable_bpfc="no"])
+ [enable_bpfc="$enableval"], [enable_bpfc="no"])
+AC_ARG_ENABLE([nfsynproxy],
+ AS_HELP_STRING([--enable-nfsynproxy], [Build SYNPROXY configuration tool]),
+ [enable_nfsynproxy="$enableval"], [enable_nfsynproxy="no"])
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-nftables], [Do not build nftables compat]),
+ [enable_nftables="$enableval"], [enable_nftables="yes"])
+AC_ARG_ENABLE([connlabel],
+ AS_HELP_STRING([--disable-connlabel],
+ [Do not build libnetfilter_conntrack]),
+ [enable_connlabel="$enableval"], [enable_connlabel="yes"])
+AC_ARG_WITH([xt-lock-name], AS_HELP_STRING([--with-xt-lock-name=PATH],
+ [Path to the xtables lock [[/run/xtables.lock]]]),
+ [xt_lock_name="$withval"],
+ [xt_lock_name="/run/xtables.lock"])
libiptc_LDFLAGS2="";
AX_CHECK_LINKER_FLAGS([-Wl,--no-as-needed],
@@ -72,9 +86,14 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) {}])],
)
LDFLAGS="$saved_LDFLAGS";
-blacklist_modules="";
+blacklist_modules=""
+blacklist_x_modules=""
+blacklist_b_modules=""
+blacklist_a_modules=""
+blacklist_4_modules=""
+blacklist_6_modules=""
-AC_CHECK_HEADERS([linux/dccp.h linux/ip_vs.h linux/magic.h linux/proc_fs.h])
+AC_CHECK_HEADERS([linux/dccp.h linux/ip_vs.h linux/magic.h linux/proc_fs.h linux/bpf.h])
if test "$ac_cv_header_linux_dccp_h" != "yes"; then
blacklist_modules="$blacklist_modules dccp";
fi;
@@ -82,16 +101,6 @@ if test "$ac_cv_header_linux_ip_vs_h" != "yes"; then
blacklist_modules="$blacklist_modules ipvs";
fi;
-PKG_CHECK_MODULES([libnetfilter_conntrack], [libnetfilter_conntrack >= 1.0.4],
- [nfconntrack=1], [nfconntrack=0])
-AM_CONDITIONAL([HAVE_LIBNETFILTER_CONNTRACK], [test "$nfconntrack" = 1])
-
-if test "$nfconntrack" -ne 1; then
- blacklist_modules="$blacklist_modules connlabel";
- echo "WARNING: libnetfilter_conntrack not found, connlabel match will not be built";
-fi;
-
-AC_SUBST([blacklist_modules])
AC_CHECK_SIZEOF([struct ip6_hdr], [], [#include <netinet/ip6.h>])
AM_CONDITIONAL([ENABLE_STATIC], [test "$enable_static" = "yes"])
@@ -102,15 +111,89 @@ AM_CONDITIONAL([ENABLE_LARGEFILE], [test "$enable_largefile" = "yes"])
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"])
+AM_CONDITIONAL([ENABLE_CONNLABEL], [test "$enable_connlabel" = "yes"])
-if test "x$enable_bpfc" = "xyes"; then
- AC_CHECK_LIB(pcap, pcap_compile,, AC_MSG_ERROR(missing libpcap library required by bpf compiler))
+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))
fi
PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0],
[nfnetlink=1], [nfnetlink=0])
AM_CONDITIONAL([HAVE_LIBNFNETLINK], [test "$nfnetlink" = 1])
+if test "x$enable_nftables" = "xyes"; then
+ PKG_CHECK_MODULES([libmnl], [libmnl >= 1.0], [mnl=1], [mnl=0])
+
+ if test "$mnl" = 0;
+ then
+ echo "*** Error: No suitable libmnl found. ***"
+ echo " Please install the 'libmnl' package"
+ echo " Or consider --disable-nftables to skip"
+ echo " iptables-compat over nftables support."
+ exit 1
+ fi
+
+ PKG_CHECK_MODULES([libnftnl], [libnftnl >= 1.0.5], [nftables=1], [nftables=0])
+
+ if test "$nftables" = 0;
+ then
+ echo "*** Error: no suitable libnftnl found. ***"
+ echo " Please install the 'libnftnl' package"
+ echo " Or consider --disable-nftables to skip"
+ echo " iptables-compat over nftables support."
+ exit 1
+ fi
+
+ 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
+fi
+
+AM_CONDITIONAL([HAVE_LIBMNL], [test "$mnl" = 1])
+AM_CONDITIONAL([HAVE_LIBNFTNL], [test "$nftables" = 1])
+
+if test "$nftables" != 1; then
+ blacklist_b_modules="$blacklist_b_modules limit mark nflog mangle"
+ blacklist_a_modules="$blacklist_a_modules mangle"
+fi
+
+if test "x$enable_connlabel" = "xyes"; then
+ PKG_CHECK_MODULES([libnetfilter_conntrack],
+ [libnetfilter_conntrack >= 1.0.6],
+ [nfconntrack=1], [nfconntrack=0])
+
+ if test "$nfconntrack" -ne 1; then
+ blacklist_modules="$blacklist_modules connlabel";
+ echo "WARNING: libnetfilter_conntrack not found, connlabel match will not be built";
+ enable_connlabel="no";
+ fi;
+else
+ blacklist_modules="$blacklist_modules connlabel";
+fi;
+
+AM_CONDITIONAL([HAVE_LIBNETFILTER_CONNTRACK], [test "$nfconntrack" = 1])
+
+AC_SUBST([blacklist_modules])
+AC_SUBST([blacklist_x_modules])
+AC_SUBST([blacklist_b_modules])
+AC_SUBST([blacklist_a_modules])
+AC_SUBST([blacklist_4_modules])
+AC_SUBST([blacklist_6_modules])
+
regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
-Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \
-Winline -pipe";
@@ -153,10 +236,14 @@ AC_SUBST([libxtables_vage])
libxtables_vmajor=$(($libxtables_vcurrent - $libxtables_vage));
AC_SUBST([libxtables_vmajor])
+AC_DEFINE_UNQUOTED([XT_LOCK_NAME], "${xt_lock_name}",
+ [Location of the iptables lock file])
+
AC_CONFIG_FILES([Makefile extensions/GNUmakefile include/Makefile
iptables/Makefile iptables/xtables.pc
- iptables/iptables.8 iptables/ip6tables.8
- iptables/iptables-extensions.8.tmpl
+ iptables/iptables.8 iptables/iptables-extensions.8.tmpl
+ iptables/iptables-save.8 iptables/iptables-restore.8
+ iptables/iptables-apply.8 iptables/iptables-xml.1
libipq/Makefile libipq/libipq.pc
libiptc/Makefile libiptc/libiptc.pc
libiptc/libip4tc.pc libiptc/libip6tc.pc
@@ -176,13 +263,17 @@ Iptables Configuration:
IPQ support: ${enable_libipq}
Large file support: ${enable_largefile}
BPF utils support: ${enable_bpfc}
+ nfsynproxy util support: ${enable_nfsynproxy}
+ nftables support: ${enable_nftables}
+ connlabel support: ${enable_connlabel}
Build parameters:
Put plugins into executable (static): ${enable_static}
Support plugins via dlopen (shared): ${enable_shared}
Installation prefix (--prefix): ${prefix}
Xtables extension directory: ${e_xtlibdir}
- Pkg-config directory: ${e_pkgconfigdir}"
+ Pkg-config directory: ${e_pkgconfigdir}
+ Xtables lock file: ${xt_lock_name}"
if [[ -n "$ksourcedir" ]]; then
echo " Kernel source directory: ${ksourcedir}"
diff --git a/etc/ethertypes b/etc/ethertypes
new file mode 100644
index 00000000..813177b7
--- /dev/null
+++ b/etc/ethertypes
@@ -0,0 +1,39 @@
+#
+# Ethernet frame types
+# This file describes some of the various Ethernet
+# protocol types that are used on Ethernet networks.
+#
+# This list could be found on:
+# http://www.iana.org/assignments/ethernet-numbers
+# http://www.iana.org/assignments/ieee-802-numbers
+#
+# <name> <hexnumber> <alias1>...<alias35> #Comment
+#
+IPv4 0800 ip ip4 # Internet IP (IPv4)
+X25 0805
+ARP 0806 ether-arp #
+FR_ARP 0808 # Frame Relay ARP [RFC1701]
+BPQ 08FF # G8BPQ AX.25 Ethernet Packet
+DEC 6000 # DEC Assigned proto
+DNA_DL 6001 # DEC DNA Dump/Load
+DNA_RC 6002 # DEC DNA Remote Console
+DNA_RT 6003 # DEC DNA Routing
+LAT 6004 # DEC LAT
+DIAG 6005 # DEC Diagnostics
+CUST 6006 # DEC Customer use
+SCA 6007 # DEC Systems Comms Arch
+TEB 6558 # Trans Ether Bridging [RFC1701]
+RAW_FR 6559 # Raw Frame Relay [RFC1701]
+RARP 8035 # Reverse ARP [RFC903]
+AARP 80F3 # Appletalk AARP
+ATALK 809B # Appletalk
+802_1Q 8100 8021q 1q 802.1q dot1q # 802.1Q Virtual LAN tagged frame
+IPX 8137 # Novell IPX
+NetBEUI 8191 # NetBEUI
+IPv6 86DD ip6 # IP version 6
+PPP 880B # PPP
+ATMMPOA 884C # MultiProtocol over ATM
+PPP_DISC 8863 # PPPoE discovery messages
+PPP_SES 8864 # PPPoE session messages
+ATMFATE 8884 # Frame-based ATM Transport over Ethernet
+LOOP 9000 loopback # loop proto
diff --git a/etc/xtables.conf b/etc/xtables.conf
new file mode 100644
index 00000000..d37b0d7c
--- /dev/null
+++ b/etc/xtables.conf
@@ -0,0 +1,75 @@
+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 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
+ }
+}
+
+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
+ }
+}
+
+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/extensions/Android.mk b/extensions/Android.mk
index 2f949016..b41cf380 100644
--- a/extensions/Android.mk
+++ b/extensions/Android.mk
@@ -4,7 +4,7 @@ LOCAL_PATH:= $(call my-dir)
MY_srcdir:=$(LOCAL_PATH)
# Exclude some modules that are problematic to compile (types/header).
-MY_excluded_modules:=TCPOPTSTRIP connlabel
+MY_excluded_modules:=TCPOPTSTRIP connlabel cgroup
MY_pfx_build_mod := $(patsubst ${MY_srcdir}/libxt_%.c,%,$(sort $(wildcard ${MY_srcdir}/libxt_*.c)))
MY_pf4_build_mod := $(patsubst ${MY_srcdir}/libipt_%.c,%,$(sort $(wildcard ${MY_srcdir}/libipt_*.c)))
@@ -19,7 +19,8 @@ MY_pf6_objs := $(patsubst %,libip6t_%.o,${MY_pf6_build_mod})
MY_warnings := \
-Wno-unused-parameter -Wno-missing-field-initializers \
-Wno-sign-compare -Wno-pointer-arith \
- -Wno-pointer-bool-conversion
+ -Wno-pointer-bool-conversion \
+ -Wno-tautological-pointer-compare
libext_suffix :=
libext_prefix := xt
diff --git a/extensions/GNUmakefile.in b/extensions/GNUmakefile.in
index 780e7150..b7a8a836 100644
--- a/extensions/GNUmakefile.in
+++ b/extensions/GNUmakefile.in
@@ -11,6 +11,7 @@ libdir = @libdir@
libexecdir = @libexecdir@
xtlibdir = @xtlibdir@
+AR = @AR@
CC = @CC@
CCLD = ${CC}
CFLAGS = @CFLAGS@
@@ -21,7 +22,7 @@ regular_CPPFLAGS = @regular_CPPFLAGS@
kinclude_CPPFLAGS = @kinclude_CPPFLAGS@
AM_CFLAGS = ${regular_CFLAGS}
-AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_builddir} -I${top_srcdir}/include ${kinclude_CPPFLAGS} @libnetfilter_conntrack_CFLAGS@
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_builddir} -I${top_srcdir}/include -I${top_srcdir} ${kinclude_CPPFLAGS} ${CPPFLAGS} @libnetfilter_conntrack_CFLAGS@ @libnftnl_CFLAGS@
AM_DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@
AM_LDFLAGS = @noundef_LDFLAGS@
@@ -39,16 +40,24 @@ endif
# Wildcard module list
#
pfx_build_mod := $(patsubst ${srcdir}/libxt_%.c,%,$(sort $(wildcard ${srcdir}/libxt_*.c)))
+pfb_build_mod := $(patsubst ${srcdir}/libebt_%.c,%,$(sort $(wildcard ${srcdir}/libebt_*.c)))
+pfa_build_mod := $(patsubst ${srcdir}/libarpt_%.c,%,$(sort $(wildcard ${srcdir}/libarpt_*.c)))
pfx_symlinks := NOTRACK state
@ENABLE_IPV4_TRUE@ pf4_build_mod := $(patsubst ${srcdir}/libipt_%.c,%,$(sort $(wildcard ${srcdir}/libipt_*.c)))
@ENABLE_IPV6_TRUE@ pf6_build_mod := $(patsubst ${srcdir}/libip6t_%.c,%,$(sort $(wildcard ${srcdir}/libip6t_*.c)))
-pfx_build_mod := $(filter-out @blacklist_modules@,${pfx_build_mod})
-pf4_build_mod := $(filter-out @blacklist_modules@,${pf4_build_mod})
-pf6_build_mod := $(filter-out @blacklist_modules@,${pf6_build_mod})
+pfx_build_mod := $(filter-out @blacklist_modules@ @blacklist_x_modules@,${pfx_build_mod})
+pfb_build_mod := $(filter-out @blacklist_modules@ @blacklist_b_modules@,${pfb_build_mod})
+pfa_build_mod := $(filter-out @blacklist_modules@ @blacklist_a_modules@,${pfa_build_mod})
+pf4_build_mod := $(filter-out @blacklist_modules@ @blacklist_4_modules@,${pf4_build_mod})
+pf6_build_mod := $(filter-out @blacklist_modules@ @blacklist_6_modules@,${pf6_build_mod})
pfx_objs := $(patsubst %,libxt_%.o,${pfx_build_mod})
+pfb_objs := $(patsubst %,libebt_%.o,${pfb_build_mod})
+pfa_objs := $(patsubst %,libarpt_%.o,${pfa_build_mod})
pf4_objs := $(patsubst %,libipt_%.o,${pf4_build_mod})
pf6_objs := $(patsubst %,libip6t_%.o,${pf6_build_mod})
pfx_solibs := $(patsubst %,libxt_%.so,${pfx_build_mod} ${pfx_symlinks})
+pfb_solibs := $(patsubst %,libebt_%.so,${pfb_build_mod})
+pfa_solibs := $(patsubst %,libarpt_%.so,${pfa_build_mod})
pf4_solibs := $(patsubst %,libipt_%.so,${pf4_build_mod})
pf6_solibs := $(patsubst %,libip6t_%.so,${pf6_build_mod})
@@ -56,13 +65,15 @@ pf6_solibs := $(patsubst %,libip6t_%.so,${pf6_build_mod})
#
# Building blocks
#
-targets := libext.a libext4.a libext6.a matches.man targets.man
+targets := libext.a libext4.a libext6.a libext_ebt.a libext_arpt.a matches.man targets.man
targets_install :=
@ENABLE_STATIC_TRUE@ libext_objs := ${pfx_objs}
+@ENABLE_STATIC_TRUE@ libext_ebt_objs := ${pfb_objs}
+@ENABLE_STATIC_TRUE@ libext_arpt_objs := ${pfa_objs}
@ENABLE_STATIC_TRUE@ libext4_objs := ${pf4_objs}
@ENABLE_STATIC_TRUE@ libext6_objs := ${pf6_objs}
-@ENABLE_STATIC_FALSE@ targets += ${pfx_solibs} ${pf4_solibs} ${pf6_solibs}
-@ENABLE_STATIC_FALSE@ targets_install += ${pfx_solibs} ${pf4_solibs} ${pf6_solibs}
+@ENABLE_STATIC_FALSE@ targets += ${pfx_solibs} ${pfb_solibs} ${pf4_solibs} ${pf6_solibs} ${pfa_solibs}
+@ENABLE_STATIC_FALSE@ targets_install += ${pfx_solibs} ${pfb_solibs} ${pf4_solibs} ${pf6_solibs} ${pfa_solibs}
.SECONDARY:
@@ -75,7 +86,7 @@ install: ${targets_install}
if test -n "${targets_install}"; then install -pm0755 $^ "${DESTDIR}${xtlibdir}/"; fi;
clean:
- rm -f *.o *.oo *.so *.a {matches,targets}.man initext.c initext4.c initext6.c;
+ rm -f *.o *.oo *.so *.a {matches,targets}.man initext.c initext4.c initext6.c initextb.c initexta.c;
rm -f .*.d .*.dd;
distclean: clean
@@ -118,6 +129,12 @@ lib%.o: ${srcdir}/lib%.c
libext.a: initext.o ${libext_objs}
${AM_VERBOSE_AR} ${AR} crs $@ $^;
+libext_ebt.a: initextb.o ${libext_ebt_objs}
+ ${AM_VERBOSE_AR} ${AR} crs $@ $^;
+
+libext_arpt.a: initexta.o ${libext_arpt_objs}
+ ${AM_VERBOSE_AR} ${AR} crs $@ $^;
+
libext4.a: initext4.o ${libext4_objs}
${AM_VERBOSE_AR} ${AR} crs $@ $^;
@@ -125,6 +142,8 @@ libext6.a: initext6.o ${libext6_objs}
${AM_VERBOSE_AR} ${AR} crs $@ $^;
initext_func := $(addprefix xt_,${pfx_build_mod})
+initextb_func := $(addprefix ebt_,${pfb_build_mod})
+initexta_func := $(addprefix arpt_,${pfa_build_mod})
initext4_func := $(addprefix ipt_,${pf4_build_mod})
initext6_func := $(addprefix ip6t_,${pf6_build_mod})
@@ -133,6 +152,16 @@ initext6_func := $(addprefix ip6t_,${pf6_build_mod})
cmp -s $@ $@.tmp || mv $@.tmp $@; \
rm -f $@.tmp;
+.initextb.dd: FORCE
+ @echo "${initextb_func}" >$@.tmp; \
+ cmp -s $@ $@.tmp || mv $@.tmp $@; \
+ rm -f $@.tmp;
+
+.initexta.dd: FORCE
+ @echo "${initexta_func}" >$@.tmp; \
+ cmp -s $@ $@.tmp || mv $@.tmp $@; \
+ rm -f $@.tmp;
+
.initext4.dd: FORCE
@echo "${initext4_func}" >$@.tmp; \
cmp -s $@ $@.tmp || mv $@.tmp $@; \
@@ -159,6 +188,38 @@ initext.c: .initext.dd
echo "}" >>$@; \
);
+initextb.c: .initextb.dd
+ ${AM_VERBOSE_GEN}
+ @( \
+ echo "" >$@; \
+ for i in ${initextb_func}; do \
+ echo "extern void lib$${i}_init(void);" >>$@; \
+ done; \
+ echo "void init_extensionsb(void);" >>$@; \
+ echo "void init_extensionsb(void)" >>$@; \
+ echo "{" >>$@; \
+ for i in ${initextb_func}; do \
+ echo " ""lib$${i}_init();" >>$@; \
+ done; \
+ echo "}" >>$@; \
+ );
+
+initexta.c: .initexta.dd
+ ${AM_VERBOSE_GEN}
+ @( \
+ echo "" >$@; \
+ for i in ${initexta_func}; do \
+ echo "extern void lib$${i}_init(void);" >>$@; \
+ done; \
+ echo "void init_extensionsa(void);" >>$@; \
+ echo "void init_extensionsa(void)" >>$@; \
+ echo "{" >>$@; \
+ for i in ${initexta_func}; do \
+ echo " ""lib$${i}_init();" >>$@; \
+ done; \
+ echo "}" >>$@; \
+ );
+
initext4.c: .initext4.dd
${AM_VERBOSE_GEN}
@( \
@@ -219,8 +280,8 @@ man_run = \
fi; \
done >$@;
-matches.man: .initext.dd .initext4.dd .initext6.dd $(wildcard ${srcdir}/lib*.man)
- $(call man_run,$(call ex_matches,${pfx_build_mod} ${pf4_build_mod} ${pf6_build_mod} ${pfx_symlinks}))
+matches.man: .initext.dd .initextb.dd .initexta.dd .initext4.dd .initext6.dd $(wildcard ${srcdir}/lib*.man)
+ $(call man_run,$(call ex_matches,${pfx_build_mod} ${pfb_build_mod} ${pfa_build_mod} ${pf4_build_mod} ${pf6_build_mod} ${pfx_symlinks}))
-targets.man: .initext.dd .initext4.dd .initext6.dd $(wildcard ${srcdir}/lib*.man)
- $(call man_run,$(call ex_targets,${pfx_build_mod} ${pf4_build_mod} ${pf6_build_mod} ${pfx_symlinks}))
+targets.man: .initext.dd .initextb.dd .initexta.dd .initext4.dd .initext6.dd $(wildcard ${srcdir}/lib*.man)
+ $(call man_run,$(call ex_targets,${pfx_build_mod} ${pfb_build_mod} ${pfa_build_mod} ${pf4_build_mod} ${pf6_build_mod} ${pfx_symlinks}))
diff --git a/extensions/libarpt_mangle.c b/extensions/libarpt_mangle.c
new file mode 100644
index 00000000..ec9b5436
--- /dev/null
+++ b/extensions/libarpt_mangle.c
@@ -0,0 +1,204 @@
+/*
+ * Arturo Borrero Gonzalez <arturo@debian.org> adapted
+ * this code to libxtables for arptables-compat in 2015
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <netinet/ether.h>
+#include <xtables.h>
+#include <linux/netfilter_arp/arpt_mangle.h>
+#include "iptables/nft.h"
+#include "iptables/nft-arp.h"
+
+static void arpmangle_print_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 struct option arpmangle_opts[] = {
+ { .name = "mangle-ip-s", .has_arg = true, .val = MANGLE_IPS },
+ { .name = "mangle-ip-d", .has_arg = true, .val = MANGLE_IPT },
+ { .name = "mangle-mac-s", .has_arg = true, .val = MANGLE_DEVS },
+ { .name = "mangle-mac-d", .has_arg = true, .val = MANGLE_DEVT },
+ { .name = "mangle-target", .has_arg = true, .val = MANGLE_TARGET },
+ XT_GETOPT_TABLEEND,
+};
+
+static void arpmangle_init(struct xt_entry_target *target)
+{
+ struct arpt_mangle *mangle = (struct arpt_mangle *)target->data;
+
+ mangle->target = NF_ACCEPT;
+}
+
+static int
+arpmangle_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **target)
+{
+ struct arpt_mangle *mangle = (struct arpt_mangle *)(*target)->data;
+ struct in_addr *ipaddr, mask;
+ struct ether_addr *macaddr;
+ const struct arpt_entry *e = (const struct arpt_entry *)entry;
+ unsigned int nr;
+ int ret = 1;
+
+ memset(&mask, 0, sizeof(mask));
+
+ switch (c) {
+ case MANGLE_IPS:
+ xtables_ipparse_any(optarg, &ipaddr, &mask, &nr);
+ mangle->u_s.src_ip.s_addr = ipaddr->s_addr;
+ free(ipaddr);
+ mangle->flags |= ARPT_MANGLE_SIP;
+ break;
+ case MANGLE_IPT:
+ xtables_ipparse_any(optarg, &ipaddr, &mask, &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(optarg);
+ 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(optarg);
+ 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(optarg, "DROP"))
+ mangle->target = NF_DROP;
+ else if (!strcmp(optarg, "ACCEPT"))
+ mangle->target = NF_ACCEPT;
+ else if (!strcmp(optarg, "CONTINUE"))
+ mangle->target = XT_CONTINUE;
+ else
+ xtables_error(PARAMETER_PROBLEM,
+ "bad target for --mangle-target");
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void arpmangle_final_check(unsigned int flags)
+{
+}
+
+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
+arpmangle_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ struct arpt_mangle *m = (struct arpt_mangle *)(target->data);
+ char buf[100];
+
+ if (m->flags & ARPT_MANGLE_SIP) {
+ if (numeric)
+ sprintf(buf, "%s",
+ xtables_ipaddr_to_numeric(&(m->u_s.src_ip)));
+ else
+ sprintf(buf, "%s",
+ xtables_ipaddr_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",
+ xtables_ipaddr_to_numeric(&(m->u_t.tgt_ip)));
+ else
+ sprintf(buf, "%s",
+ xtables_ipaddr_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 struct xtables_target arpmangle_target = {
+ .name = "mangle",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_ARP,
+ .size = XT_ALIGN(sizeof(struct arpt_mangle)),
+ .userspacesize = XT_ALIGN(sizeof(struct arpt_mangle)),
+ .help = arpmangle_print_help,
+ .init = arpmangle_init,
+ .parse = arpmangle_parse,
+ .final_check = arpmangle_final_check,
+ .print = arpmangle_print,
+ .extra_opts = arpmangle_opts,
+};
+
+void _init(void)
+{
+ xtables_register_target(&arpmangle_target);
+}
diff --git a/extensions/libebt_802_3.c b/extensions/libebt_802_3.c
new file mode 100644
index 00000000..f05d02ea
--- /dev/null
+++ b/extensions/libebt_802_3.c
@@ -0,0 +1,133 @@
+/* 802_3
+ *
+ * Author:
+ * Chris Vitale <csv@bluetail.com>
+ *
+ * May 2003
+ *
+ * Adapted by Arturo Borrero Gonzalez <arturo@debian.org>
+ * to use libxtables for ebtables-compat
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <xtables.h>
+#include <linux/netfilter_bridge/ebt_802_3.h>
+
+#define _802_3_SAP '1'
+#define _802_3_TYPE '2'
+
+static const struct option br802_3_opts[] = {
+ { .name = "802_3-sap", .has_arg = true, .val = _802_3_SAP },
+ { .name = "802_3-type", .has_arg = true, .val = _802_3_TYPE },
+ XT_GETOPT_TABLEEND,
+};
+
+static void br802_3_print_help(void)
+{
+ printf(
+"802_3 options:\n"
+"--802_3-sap [!] protocol : 802.3 DSAP/SSAP- 1 byte value (hex)\n"
+" DSAP and SSAP are always the same. One SAP applies to both fields\n"
+"--802_3-type [!] protocol : 802.3 SNAP Type- 2 byte value (hex)\n"
+" Type implies SAP value 0xaa\n");
+}
+
+static void br802_3_init(struct xt_entry_match *match)
+{
+ struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
+
+ info->invflags = 0;
+ info->bitmask = 0;
+}
+
+static int
+br802_3_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_match **match)
+{
+ struct ebt_802_3_info *info = (struct ebt_802_3_info *) (*match)->data;
+ unsigned int i;
+ char *end;
+
+ switch (c) {
+ case _802_3_SAP:
+ if (invert)
+ info->invflags |= EBT_802_3_SAP;
+ i = strtoul(optarg, &end, 16);
+ if (i > 255 || *end != '\0')
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with specified "
+ "sap hex value, %x",i);
+ info->sap = i; /* one byte, so no byte order worries */
+ info->bitmask |= EBT_802_3_SAP;
+ break;
+ case _802_3_TYPE:
+ if (invert)
+ info->invflags |= EBT_802_3_TYPE;
+ i = strtoul(optarg, &end, 16);
+ if (i > 65535 || *end != '\0') {
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the specified "
+ "type hex value, %x",i);
+ }
+ info->type = htons(i);
+ info->bitmask |= EBT_802_3_TYPE;
+ break;
+ default:
+ return 0;
+ }
+
+ *flags |= info->bitmask;
+ return 1;
+}
+
+static void
+br802_3_final_check(unsigned int flags)
+{
+ if (!flags)
+ xtables_error(PARAMETER_PROBLEM,
+ "You must specify proper arguments");
+}
+
+static void br802_3_print(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
+
+ if (info->bitmask & EBT_802_3_SAP) {
+ printf("--802_3-sap ");
+ if (info->invflags & EBT_802_3_SAP)
+ printf("! ");
+ printf("0x%.2x ", info->sap);
+ }
+ if (info->bitmask & EBT_802_3_TYPE) {
+ printf("--802_3-type ");
+ if (info->invflags & EBT_802_3_TYPE)
+ printf("! ");
+ printf("0x%.4x ", ntohs(info->type));
+ }
+}
+
+static struct xtables_match br802_3_match =
+{
+ .name = "802_3",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_BRIDGE,
+ .size = XT_ALIGN(sizeof(struct ebt_802_3_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct ebt_802_3_info)),
+ .init = br802_3_init,
+ .help = br802_3_print_help,
+ .parse = br802_3_parse,
+ .final_check = br802_3_final_check,
+ .print = br802_3_print,
+ .extra_opts = br802_3_opts,
+};
+
+void _init(void)
+{
+ xtables_register_match(&br802_3_match);
+}
diff --git a/extensions/libebt_ip.c b/extensions/libebt_ip.c
new file mode 100644
index 00000000..4ca63e93
--- /dev/null
+++ b/extensions/libebt_ip.c
@@ -0,0 +1,312 @@
+/* ebt_ip
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * Changes:
+ * added ip-sport and ip-dport; parsing of port arguments is
+ * based on code from iptables-1.2.7a
+ * Innominate Security Technologies AG <mhopf@innominate.com>
+ * September, 2002
+ *
+ * Adapted by Arturo Borrero Gonzalez <arturo@debian.org>
+ * to use libxtables for ebtables-compat in 2015.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <xtables.h>
+#include <linux/netfilter_bridge/ebt_ip.h>
+
+#define IP_SOURCE '1'
+#define IP_DEST '2'
+#define IP_EBT_TOS '3' /* include/bits/in.h seems to already define IP_TOS */
+#define IP_PROTO '4'
+#define IP_SPORT '5'
+#define IP_DPORT '6'
+
+static const struct option brip_opts[] = {
+ { .name = "ip-source", .has_arg = true, .val = IP_SOURCE },
+ { .name = "ip-src", .has_arg = true, .val = IP_SOURCE },
+ { .name = "ip-destination", .has_arg = true, .val = IP_DEST },
+ { .name = "ip-dst", .has_arg = true, .val = IP_DEST },
+ { .name = "ip-tos", .has_arg = true, .val = IP_EBT_TOS },
+ { .name = "ip-protocol", .has_arg = true, .val = IP_PROTO },
+ { .name = "ip-proto", .has_arg = true, .val = IP_PROTO },
+ { .name = "ip-source-port", .has_arg = true, .val = IP_SPORT },
+ { .name = "ip-sport", .has_arg = true, .val = IP_SPORT },
+ { .name = "ip-destination-port",.has_arg = true, .val = IP_DPORT },
+ { .name = "ip-dport", .has_arg = true, .val = IP_DPORT },
+ XT_GETOPT_TABLEEND,
+};
+
+static void brip_print_help(void)
+{
+ printf(
+"ip options:\n"
+"--ip-src [!] address[/mask]: ip source specification\n"
+"--ip-dst [!] address[/mask]: ip destination specification\n"
+"--ip-tos [!] tos : ip tos specification\n"
+"--ip-proto [!] protocol : ip protocol specification\n"
+"--ip-sport [!] port[:port] : tcp/udp source port or port range\n"
+"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n");
+}
+
+static void brip_init(struct xt_entry_match *match)
+{
+ struct ebt_ip_info *info = (struct ebt_ip_info *)match->data;
+
+ info->invflags = 0;
+ info->bitmask = 0;
+}
+
+static void
+parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
+{
+ char *buffer;
+ char *cp;
+
+ buffer = strdup(portstring);
+ if ((cp = strchr(buffer, ':')) == NULL)
+ ports[0] = ports[1] = xtables_parse_port(buffer, NULL);
+ else {
+ *cp = '\0';
+ cp++;
+
+ ports[0] = buffer[0] ? xtables_parse_port(buffer, NULL) : 0;
+ ports[1] = cp[0] ? xtables_parse_port(cp, NULL) : 0xFFFF;
+
+ if (ports[0] > ports[1])
+ xtables_error(PARAMETER_PROBLEM,
+ "invalid portrange (min > max)");
+ }
+ free(buffer);
+}
+
+/* original code from ebtables: useful_functions.c */
+static int undot_ip(char *ip, unsigned char *ip2)
+{
+ char *p, *q, *end;
+ long int onebyte;
+ int i;
+ char buf[20];
+
+ strncpy(buf, ip, sizeof(buf) - 1);
+
+ p = buf;
+ for (i = 0; i < 3; i++) {
+ if ((q = strchr(p, '.')) == NULL)
+ return -1;
+ *q = '\0';
+ onebyte = strtol(p, &end, 10);
+ if (*end != '\0' || onebyte > 255 || onebyte < 0)
+ return -1;
+ ip2[i] = (unsigned char)onebyte;
+ p = q + 1;
+ }
+
+ onebyte = strtol(p, &end, 10);
+ if (*end != '\0' || onebyte > 255 || onebyte < 0)
+ return -1;
+ ip2[3] = (unsigned char)onebyte;
+
+ return 0;
+}
+
+static int ip_mask(char *mask, unsigned char *mask2)
+{
+ char *end;
+ long int bits;
+ uint32_t mask22;
+
+ if (undot_ip(mask, mask2)) {
+ /* not the /a.b.c.e format, maybe the /x format */
+ bits = strtol(mask, &end, 10);
+ if (*end != '\0' || bits > 32 || bits < 0)
+ return -1;
+ if (bits != 0) {
+ mask22 = htonl(0xFFFFFFFF << (32 - bits));
+ memcpy(mask2, &mask22, 4);
+ } else {
+ mask22 = 0xFFFFFFFF;
+ memcpy(mask2, &mask22, 4);
+ }
+ }
+ return 0;
+}
+
+static void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
+{
+ char *p;
+
+ /* first the mask */
+ if ((p = strrchr(address, '/')) != NULL) {
+ *p = '\0';
+ if (ip_mask(p + 1, (unsigned char *)msk)) {
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the IP mask '%s'", p + 1);
+ return;
+ }
+ } else
+ *msk = 0xFFFFFFFF;
+
+ if (undot_ip(address, (unsigned char *)addr)) {
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the IP address '%s'", address);
+ return;
+ }
+ *addr = *addr & *msk;
+}
+
+static int
+brip_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_match **match)
+{
+ struct ebt_ip_info *info = (struct ebt_ip_info *)(*match)->data;
+
+ switch (c) {
+ case IP_SOURCE:
+ if (invert)
+ info->invflags |= EBT_IP_SOURCE;
+ ebt_parse_ip_address(optarg, &info->saddr, &info->smsk);
+ info->bitmask |= EBT_IP_SOURCE;
+ break;
+ case IP_DEST:
+ if (invert)
+ info->invflags |= EBT_IP_DEST;
+ ebt_parse_ip_address(optarg, &info->daddr, &info->dmsk);
+ info->bitmask |= EBT_IP_DEST;
+ break;
+ case IP_SPORT:
+ if (invert)
+ info->invflags |= EBT_IP_SPORT;
+ parse_port_range(NULL, optarg, info->sport);
+ info->bitmask |= EBT_IP_SPORT;
+ break;
+ case IP_DPORT:
+ if (invert)
+ info->invflags |= EBT_IP_DPORT;
+ parse_port_range(NULL, optarg, info->dport);
+ info->bitmask |= EBT_IP_DPORT;
+ break;
+ case IP_EBT_TOS:
+ if (invert)
+ info->invflags |= EBT_IP_TOS;
+ if (!xtables_strtoul(optarg, NULL, (uintmax_t *)&info->tos,
+ 0, 255))
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with specified IP tos");
+ info->bitmask |= EBT_IP_TOS;
+ break;
+ case IP_PROTO:
+ if (invert)
+ info->invflags |= EBT_IP_PROTO;
+ info->protocol = xtables_parse_protocol(optarg);
+ if (info->protocol == -1)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unknown specified IP protocol - %s",
+ optarg);
+ info->bitmask |= EBT_IP_PROTO;
+ break;
+ default:
+ return 0;
+ }
+
+ *flags |= info->bitmask;
+ return 1;
+}
+
+static void brip_final_check(unsigned int flags)
+{
+ if (!flags)
+ xtables_error(PARAMETER_PROBLEM,
+ "You must specify proper arguments");
+}
+
+static void print_port_range(uint16_t *ports)
+{
+ if (ports[0] == ports[1])
+ printf("%d ", ports[0]);
+ else
+ printf("%d:%d ", ports[0], ports[1]);
+}
+
+static void brip_print(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ struct ebt_ip_info *info = (struct ebt_ip_info *)match->data;
+ struct in_addr *addrp, *maskp;
+
+ if (info->bitmask & EBT_IP_SOURCE) {
+ printf("--ip-src ");
+ if (info->invflags & EBT_IP_SOURCE)
+ printf("! ");
+ addrp = (struct in_addr *)&info->saddr;
+ maskp = (struct in_addr *)&info->smsk;
+ printf("%s%s ", xtables_ipaddr_to_numeric(addrp),
+ xtables_ipmask_to_numeric(maskp));
+ }
+ if (info->bitmask & EBT_IP_DEST) {
+ printf("--ip-dst ");
+ if (info->invflags & EBT_IP_DEST)
+ printf("! ");
+ addrp = (struct in_addr *)&info->daddr;
+ maskp = (struct in_addr *)&info->dmsk;
+ printf("%s%s ", xtables_ipaddr_to_numeric(addrp),
+ xtables_ipmask_to_numeric(maskp));
+ }
+ if (info->bitmask & EBT_IP_TOS) {
+ printf("--ip-tos ");
+ if (info->invflags & EBT_IP_TOS)
+ printf("! ");
+ printf("0x%02X ", info->tos);
+ }
+ if (info->bitmask & EBT_IP_PROTO) {
+ struct protoent *pe;
+
+ printf("--ip-proto ");
+ if (info->invflags & EBT_IP_PROTO)
+ printf("! ");
+ pe = getprotobynumber(info->protocol);
+ if (pe == NULL) {
+ printf("%d ", info->protocol);
+ } else {
+ printf("%s ", pe->p_name);
+ }
+ }
+ if (info->bitmask & EBT_IP_SPORT) {
+ printf("--ip-sport ");
+ if (info->invflags & EBT_IP_SPORT)
+ printf("! ");
+ print_port_range(info->sport);
+ }
+ if (info->bitmask & EBT_IP_DPORT) {
+ printf("--ip-dport ");
+ if (info->invflags & EBT_IP_DPORT)
+ printf("! ");
+ print_port_range(info->dport);
+ }
+}
+
+static struct xtables_match brip_match = {
+ .name = "ip",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_BRIDGE,
+ .size = XT_ALIGN(sizeof(struct ebt_ip_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct ebt_ip_info)),
+ .init = brip_init,
+ .help = brip_print_help,
+ .parse = brip_parse,
+ .final_check = brip_final_check,
+ .print = brip_print,
+ .extra_opts = brip_opts,
+};
+
+void _init(void)
+{
+ xtables_register_match(&brip_match);
+}
diff --git a/extensions/libebt_limit.c b/extensions/libebt_limit.c
new file mode 100644
index 00000000..6b9bb16f
--- /dev/null
+++ b/extensions/libebt_limit.c
@@ -0,0 +1,179 @@
+/* ebt_limit
+ *
+ * Authors:
+ * Tom Marshall <tommy@home.tig-grr.com>
+ *
+ * Mostly copied from iptables' limit match.
+ *
+ * September, 2003
+ *
+ * Translated to use libxtables for ebtables-compat in 2015 by
+ * Arturo Borrero Gonzalez <arturo@debian.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <xtables.h>
+#include <linux/netfilter_bridge/ebt_limit.h>
+#include "iptables/nft.h"
+#include "iptables/nft-bridge.h"
+
+#define EBT_LIMIT_AVG "3/hour"
+#define EBT_LIMIT_BURST 5
+
+#define FLAG_LIMIT 0x01
+#define FLAG_LIMIT_BURST 0x02
+#define ARG_LIMIT '1'
+#define ARG_LIMIT_BURST '2'
+
+static struct option brlimit_opts[] =
+{
+ { .name = "limit", .has_arg = true, .val = ARG_LIMIT },
+ { .name = "limit-burst",.has_arg = true, .val = ARG_LIMIT_BURST },
+ XT_GETOPT_TABLEEND,
+};
+
+static void brlimit_print_help(void)
+{
+ printf(
+"limit options:\n"
+"--limit avg : max average match rate: default "EBT_LIMIT_AVG"\n"
+" [Packets per second unless followed by \n"
+" /sec /minute /hour /day postfixes]\n"
+"--limit-burst number : number to match in a burst, -1 < number < 10001,\n"
+" default %u\n", EBT_LIMIT_BURST);
+}
+
+static int parse_rate(const char *rate, uint32_t *val)
+{
+ const char *delim;
+ uint32_t r;
+ uint32_t mult = 1; /* Seconds by default. */
+
+ delim = strchr(rate, '/');
+ if (delim) {
+ if (strlen(delim+1) == 0)
+ return 0;
+
+ if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
+ mult = 1;
+ else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+ mult = 60;
+ else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+ mult = 60*60;
+ else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+ mult = 24*60*60;
+ else
+ return 0;
+ }
+ r = atoi(rate);
+ if (!r)
+ return 0;
+
+ /* This would get mapped to infinite (1/day is minimum they
+ can specify, so we're ok at that end). */
+ if (r / mult > EBT_LIMIT_SCALE)
+ return 0;
+
+ *val = EBT_LIMIT_SCALE * mult / r;
+ return 1;
+}
+
+static void brlimit_init(struct xt_entry_match *match)
+{
+ struct ebt_limit_info *r = (struct ebt_limit_info *)match->data;
+
+ parse_rate(EBT_LIMIT_AVG, &r->avg);
+ r->burst = EBT_LIMIT_BURST;
+}
+
+static int brlimit_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_match **match)
+{
+ struct ebt_limit_info *r = (struct ebt_limit_info *)(*match)->data;
+ uintmax_t num;
+
+ switch (c) {
+ case ARG_LIMIT:
+ EBT_CHECK_OPTION(flags, FLAG_LIMIT);
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --limit");
+ if (!parse_rate(optarg, &r->avg))
+ xtables_error(PARAMETER_PROBLEM,
+ "bad rate `%s'", optarg);
+ break;
+ case ARG_LIMIT_BURST:
+ EBT_CHECK_OPTION(flags, FLAG_LIMIT_BURST);
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --limit-burst");
+ if (!xtables_strtoul(optarg, NULL, &num, 0, 10000))
+ xtables_error(PARAMETER_PROBLEM,
+ "bad --limit-burst `%s'", optarg);
+ r->burst = num;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+struct rates
+{
+ const char *name;
+ uint32_t mult;
+};
+
+static struct rates g_rates[] =
+{
+ { "day", EBT_LIMIT_SCALE*24*60*60 },
+ { "hour", EBT_LIMIT_SCALE*60*60 },
+ { "min", EBT_LIMIT_SCALE*60 },
+ { "sec", EBT_LIMIT_SCALE }
+};
+
+static void print_rate(uint32_t period)
+{
+ unsigned int i;
+
+ for (i = 1; i < sizeof(g_rates)/sizeof(struct rates); i++)
+ if (period > g_rates[i].mult ||
+ g_rates[i].mult/period < g_rates[i].mult%period)
+ break;
+
+ printf("%u/%s ", g_rates[i-1].mult / period, g_rates[i-1].name);
+}
+
+static void brlimit_print(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ struct ebt_limit_info *r = (struct ebt_limit_info *)match->data;
+
+ printf("--limit ");
+ print_rate(r->avg);
+ printf("--limit-burst %u ", r->burst);
+}
+
+static struct xtables_match brlimit_match = {
+ .name = "limit",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_BRIDGE,
+ .size = XT_ALIGN(sizeof(struct ebt_limit_info)),
+ .userspacesize = offsetof(struct ebt_limit_info, prev),
+ .init = brlimit_init,
+ .help = brlimit_print_help,
+ .parse = brlimit_parse,
+ .print = brlimit_print,
+ .extra_opts = brlimit_opts,
+};
+
+void _init(void)
+{
+ xtables_register_match(&brlimit_match);
+}
diff --git a/extensions/libebt_log.c b/extensions/libebt_log.c
new file mode 100644
index 00000000..0799185d
--- /dev/null
+++ b/extensions/libebt_log.c
@@ -0,0 +1,197 @@
+/*
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Giuseppe Longo <giuseppelng@gmail.com> adapted the original code to the
+ * xtables-compat environment in 2015.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <getopt.h>
+#include <xtables.h>
+#include <linux/netfilter_bridge/ebt_log.h>
+
+#define LOG_DEFAULT_LEVEL LOG_INFO
+
+#define LOG_PREFIX '1'
+#define LOG_LEVEL '2'
+#define LOG_ARP '3'
+#define LOG_IP '4'
+#define LOG_LOG '5'
+#define LOG_IP6 '6'
+
+typedef struct _code {
+ char *c_name;
+ int c_val;
+} CODE;
+
+static CODE eight_priority[] = {
+ { "emerg", LOG_EMERG },
+ { "alert", LOG_ALERT },
+ { "crit", LOG_CRIT },
+ { "error", LOG_ERR },
+ { "warning", LOG_WARNING },
+ { "notice", LOG_NOTICE },
+ { "info", LOG_INFO },
+ { "debug", LOG_DEBUG }
+};
+
+static int name_to_loglevel(const char *arg)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (!strcmp(arg, eight_priority[i].c_name))
+ return eight_priority[i].c_val;
+
+ /* return bad loglevel */
+ return 9;
+}
+
+static const struct option brlog_opts[] = {
+ { .name = "log-prefix", .has_arg = true, .val = LOG_PREFIX },
+ { .name = "log-level", .has_arg = true, .val = LOG_LEVEL },
+ { .name = "log-arp", .has_arg = false, .val = LOG_ARP },
+ { .name = "log-ip", .has_arg = false, .val = LOG_IP },
+ { .name = "log", .has_arg = false, .val = LOG_LOG },
+ { .name = "log-ip6", .has_arg = false, .val = LOG_IP6 },
+ XT_GETOPT_TABLEEND,
+};
+
+static void brlog_help(void)
+{
+ int i;
+
+ printf(
+"log options:\n"
+"--log : use this if you're not specifying anything\n"
+"--log-level level : level = [1-8] or a string\n"
+"--log-prefix prefix : max. %d chars.\n"
+"--log-ip : put ip info. in the log for ip packets\n"
+"--log-arp : put (r)arp info. in the log for (r)arp packets\n"
+"--log-ip6 : put ip6 info. in the log for ip6 packets\n"
+ , EBT_LOG_PREFIX_SIZE - 1);
+ for (i = 0; i < 8; i++)
+ printf("%d = %s\n", eight_priority[i].c_val,
+ eight_priority[i].c_name);
+}
+
+static void brlog_init(struct xt_entry_target *t)
+{
+ struct ebt_log_info *loginfo = (struct ebt_log_info *)t->data;
+
+ loginfo->bitmask = 0;
+ loginfo->prefix[0] = '\0';
+ loginfo->loglevel = LOG_NOTICE;
+}
+
+static int brlog_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **target)
+{
+ struct ebt_log_info *loginfo = (struct ebt_log_info *)(*target)->data;
+ long int i;
+ char *end;
+
+ switch (c) {
+ case LOG_PREFIX:
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unexpected `!` after --log-prefix");
+ if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "Prefix too long");
+ if (strchr(optarg, '\"'))
+ xtables_error(PARAMETER_PROBLEM,
+ "Use of \\\" is not allowed"
+ " in the prefix");
+ strcpy((char *)loginfo->prefix, (char *)optarg);
+ break;
+ case LOG_LEVEL:
+ i = strtol(optarg, &end, 16);
+ if (*end != '\0' || i < 0 || i > 7)
+ loginfo->loglevel = name_to_loglevel(optarg);
+ else
+ loginfo->loglevel = i;
+
+ if (loginfo->loglevel == 9)
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the log-level");
+ break;
+ case LOG_IP:
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --log-ip");
+ loginfo->bitmask |= EBT_LOG_IP;
+ break;
+ case LOG_ARP:
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --log-arp");
+ loginfo->bitmask |= EBT_LOG_ARP;
+ case LOG_LOG:
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --log");
+ break;
+ case LOG_IP6:
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --log-ip6");
+ loginfo->bitmask |= EBT_LOG_IP6;
+ break;
+ default:
+ return 0;
+ }
+
+ *flags |= loginfo->bitmask;
+ return 1;
+}
+
+static void brlog_final_check(unsigned int flags)
+{
+}
+
+static void brlog_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ struct ebt_log_info *loginfo = (struct ebt_log_info *)target->data;
+
+ printf("--log-level %s --log-prefix \"%s\"",
+ eight_priority[loginfo->loglevel].c_name,
+ loginfo->prefix);
+
+ if (loginfo->bitmask & EBT_LOG_IP)
+ printf(" --log-ip");
+ if (loginfo->bitmask & EBT_LOG_ARP)
+ printf(" --log-arp");
+ if (loginfo->bitmask & EBT_LOG_IP6)
+ printf(" --log-ip6");
+ printf(" ");
+}
+
+static struct xtables_target brlog_target = {
+ .name = "log",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_BRIDGE,
+ .size = XT_ALIGN(sizeof(struct ebt_log_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct ebt_log_info)),
+ .init = brlog_init,
+ .help = brlog_help,
+ .parse = brlog_parse,
+ .final_check = brlog_final_check,
+ .print = brlog_print,
+ .extra_opts = brlog_opts,
+};
+
+void _init(void)
+{
+ xtables_register_target(&brlog_target);
+}
diff --git a/extensions/libebt_mark.c b/extensions/libebt_mark.c
new file mode 100644
index 00000000..a1a208c3
--- /dev/null
+++ b/extensions/libebt_mark.c
@@ -0,0 +1,191 @@
+/* ebt_mark
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2002, September 2006
+ *
+ * Adapted by Arturo Borrero Gonzalez <arturo@debian.org>
+ * to use libxtables for ebtables-compat in 2015.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <xtables.h>
+#include <linux/netfilter_bridge/ebt_mark_t.h>
+#include "iptables/nft.h"
+#include "iptables/nft-bridge.h"
+
+static int mark_supplied;
+
+#define MARK_TARGET '1'
+#define MARK_SETMARK '2'
+#define MARK_ORMARK '3'
+#define MARK_ANDMARK '4'
+#define MARK_XORMARK '5'
+static struct option brmark_opts[] = {
+ { .name = "mark-target",.has_arg = true, .val = MARK_TARGET },
+ /* an oldtime messup, we should have always used the scheme
+ * <extension-name>-<option> */
+ { .name = "set-mark", .has_arg = true, .val = MARK_SETMARK },
+ { .name = "mark-set", .has_arg = true, .val = MARK_SETMARK },
+ { .name = "mark-or", .has_arg = true, .val = MARK_ORMARK },
+ { .name = "mark-and", .has_arg = true, .val = MARK_ANDMARK },
+ { .name = "mark-xor", .has_arg = true, .val = MARK_XORMARK },
+ XT_GETOPT_TABLEEND,
+};
+
+static void brmark_print_help(void)
+{
+ printf(
+ "mark target options:\n"
+ " --mark-set value : Set nfmark value\n"
+ " --mark-or value : Or nfmark with value (nfmark |= value)\n"
+ " --mark-and value : And nfmark with value (nfmark &= value)\n"
+ " --mark-xor value : Xor nfmark with value (nfmark ^= value)\n"
+ " --mark-target target : ACCEPT, DROP, RETURN or CONTINUE\n");
+}
+
+static void brmark_init(struct xt_entry_target *target)
+{
+ struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)target->data;
+
+ info->target = EBT_ACCEPT;
+ info->mark = 0;
+ mark_supplied = 0;
+}
+
+#define OPT_MARK_TARGET 0x01
+#define OPT_MARK_SETMARK 0x02
+#define OPT_MARK_ORMARK 0x04
+#define OPT_MARK_ANDMARK 0x08
+#define OPT_MARK_XORMARK 0x10
+
+static int
+brmark_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **target)
+{
+ struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)
+ (*target)->data;
+ char *end;
+ uint32_t mask;
+
+ switch (c) {
+ case MARK_TARGET:
+ { unsigned int tmp;
+ EBT_CHECK_OPTION(flags, OPT_MARK_TARGET);
+ if (ebt_fill_target(optarg, &tmp))
+ xtables_error(PARAMETER_PROBLEM,
+ "Illegal --mark-target target");
+ /* the 4 lsb are left to designate the target */
+ info->target = (info->target & ~EBT_VERDICT_BITS) |
+ (tmp & EBT_VERDICT_BITS);
+ }
+ return 1;
+ case MARK_SETMARK:
+ EBT_CHECK_OPTION(flags, OPT_MARK_SETMARK);
+ mask = (OPT_MARK_ORMARK|OPT_MARK_ANDMARK|OPT_MARK_XORMARK);
+ if (*flags & mask)
+ xtables_error(PARAMETER_PROBLEM,
+ "--mark-set cannot be used together with"
+ " specific --mark option");
+ info->target = (info->target & EBT_VERDICT_BITS) |
+ MARK_SET_VALUE;
+ break;
+ case MARK_ORMARK:
+ EBT_CHECK_OPTION(flags, OPT_MARK_ORMARK);
+ mask = (OPT_MARK_SETMARK|OPT_MARK_ANDMARK|OPT_MARK_XORMARK);
+ if (*flags & mask)
+ xtables_error(PARAMETER_PROBLEM,
+ "--mark-or cannot be used together with"
+ " specific --mark option");
+ info->target = (info->target & EBT_VERDICT_BITS) |
+ MARK_OR_VALUE;
+ break;
+ case MARK_ANDMARK:
+ EBT_CHECK_OPTION(flags, OPT_MARK_ANDMARK);
+ mask = (OPT_MARK_SETMARK|OPT_MARK_ORMARK|OPT_MARK_XORMARK);
+ if (*flags & mask)
+ xtables_error(PARAMETER_PROBLEM,
+ "--mark-and cannot be used together with"
+ " specific --mark option");
+ info->target = (info->target & EBT_VERDICT_BITS) |
+ MARK_AND_VALUE;
+ break;
+ case MARK_XORMARK:
+ EBT_CHECK_OPTION(flags, OPT_MARK_XORMARK);
+ mask = (OPT_MARK_SETMARK|OPT_MARK_ANDMARK|OPT_MARK_ORMARK);
+ if (*flags & mask)
+ xtables_error(PARAMETER_PROBLEM,
+ "--mark-xor cannot be used together with"
+ " specific --mark option");
+ info->target = (info->target & EBT_VERDICT_BITS) |
+ MARK_XOR_VALUE;
+ break;
+ default:
+ return 0;
+ }
+ /* mutual code */
+ info->mark = strtoul(optarg, &end, 0);
+ if (*end != '\0' || end == optarg)
+ xtables_error(PARAMETER_PROBLEM, "Bad MARK value '%s'",
+ optarg);
+
+ mark_supplied = 1;
+ return 1;
+}
+
+static void brmark_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)target->data;
+ int tmp;
+
+ tmp = info->target & ~EBT_VERDICT_BITS;
+ if (tmp == MARK_SET_VALUE)
+ printf("--mark-set");
+ else if (tmp == MARK_OR_VALUE)
+ printf("--mark-or");
+ else if (tmp == MARK_XOR_VALUE)
+ printf("--mark-xor");
+ else if (tmp == MARK_AND_VALUE)
+ printf("--mark-and");
+ else
+ xtables_error(PARAMETER_PROBLEM, "Unknown mark action");
+
+ printf(" 0x%lx", info->mark);
+ tmp = info->target | ~EBT_VERDICT_BITS;
+ printf(" --mark-target %s", ebt_target_name(tmp));
+}
+
+static void brmark_final_check(unsigned int flags)
+{
+ if (mark_supplied == 0)
+ xtables_error(PARAMETER_PROBLEM, "No mark value supplied");
+
+ if (!flags)
+ xtables_error(PARAMETER_PROBLEM,
+ "You must specify some option");
+}
+
+static struct xtables_target brmark_target = {
+ .name = "mark",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_BRIDGE,
+ .size = XT_ALIGN(sizeof(struct ebt_mark_t_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct ebt_mark_t_info)),
+ .help = brmark_print_help,
+ .init = brmark_init,
+ .parse = brmark_parse,
+ .final_check = brmark_final_check,
+ .print = brmark_print,
+ .extra_opts = brmark_opts,
+};
+
+void _init(void)
+{
+ xtables_register_target(&brmark_target);
+}
diff --git a/extensions/libebt_mark_m.c b/extensions/libebt_mark_m.c
new file mode 100644
index 00000000..ab9d2344
--- /dev/null
+++ b/extensions/libebt_mark_m.c
@@ -0,0 +1,118 @@
+/* ebt_mark_m
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2002
+ *
+ * Adapted by Arturo Borrero Gonzalez <arturo@debian.org>
+ * to use libxtables for ebtables-compat in 2015.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <xtables.h>
+#include <linux/netfilter_bridge/ebt_mark_m.h>
+
+#define MARK '1'
+
+static struct option brmark_m_opts[] = {
+ { .name = "mark", .has_arg = true, .val = MARK },
+ XT_GETOPT_TABLEEND,
+};
+
+static void brmark_m_print_help(void)
+{
+ printf(
+"mark option:\n"
+"--mark [!] [value][/mask]: Match nfmask value (see man page)\n");
+}
+
+static void brmark_m_init(struct xt_entry_match *match)
+{
+ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *)match->data;
+
+ info->mark = 0;
+ info->mask = 0;
+ info->invert = 0;
+ info->bitmask = 0;
+}
+
+#define OPT_MARK 0x01
+static int
+brmark_m_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_match **match)
+{
+ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *)
+ (*match)->data;
+ char *end;
+
+ switch (c) {
+ case MARK:
+ if (invert)
+ info->invert = 1;
+ info->mark = strtoul(optarg, &end, 0);
+ info->bitmask = EBT_MARK_AND;
+ if (*end == '/') {
+ if (end == optarg)
+ info->bitmask = EBT_MARK_OR;
+ info->mask = strtoul(end+1, &end, 0);
+ } else {
+ info->mask = 0xffffffff;
+ }
+ if (*end != '\0' || end == optarg)
+ xtables_error(PARAMETER_PROBLEM, "Bad mark value '%s'",
+ optarg);
+ break;
+ default:
+ return 0;
+ }
+
+ *flags |= info->bitmask;
+ return 1;
+}
+
+static void brmark_m_final_check(unsigned int flags)
+{
+ if (!flags)
+ xtables_error(PARAMETER_PROBLEM,
+ "You must specify proper arguments");
+}
+
+static void brmark_m_print(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *)match->data;
+
+ printf("--mark ");
+ if (info->invert)
+ printf("! ");
+ if (info->bitmask == EBT_MARK_OR)
+ printf("/0x%lx ", info->mask);
+ else if (info->mask != 0xffffffff)
+ printf("0x%lx/0x%lx ", info->mark, info->mask);
+ else
+ printf("0x%lx ", info->mark);
+}
+
+static struct xtables_match brmark_m_match = {
+ .name = "mark_m",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_BRIDGE,
+ .size = XT_ALIGN(sizeof(struct ebt_mark_m_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct ebt_mark_m_info)),
+ .init = brmark_m_init,
+ .help = brmark_m_print_help,
+ .parse = brmark_m_parse,
+ .final_check = brmark_m_final_check,
+ .print = brmark_m_print,
+ .extra_opts = brmark_m_opts,
+};
+
+void _init(void)
+{
+ xtables_register_match(&brmark_m_match);
+}
diff --git a/extensions/libebt_nflog.c b/extensions/libebt_nflog.c
new file mode 100644
index 00000000..fef71960
--- /dev/null
+++ b/extensions/libebt_nflog.c
@@ -0,0 +1,144 @@
+/* ebt_nflog
+ *
+ * Authors:
+ * Peter Warasin <peter@endian.com>
+ *
+ * February, 2008
+ *
+ * Based on:
+ * ebt_ulog.c, (C) 2004, Bart De Schuymer <bdschuym@pandora.be>
+ * libxt_NFLOG.c
+ *
+ * Adapted to libxtables for ebtables-compat in 2015 by
+ * Arturo Borrero Gonzalez <arturo@debian.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <xtables.h>
+#include "iptables/nft.h"
+#include "iptables/nft-bridge.h"
+#include <linux/netfilter_bridge/ebt_nflog.h>
+
+enum {
+ NFLOG_GROUP = 0x1,
+ NFLOG_PREFIX = 0x2,
+ NFLOG_RANGE = 0x4,
+ NFLOG_THRESHOLD = 0x8,
+ NFLOG_NFLOG = 0x16,
+};
+
+static struct option brnflog_opts[] = {
+ { .name = "nflog-group", .has_arg = true, .val = NFLOG_GROUP},
+ { .name = "nflog-prefix", .has_arg = true, .val = NFLOG_PREFIX},
+ { .name = "nflog-range", .has_arg = true, .val = NFLOG_RANGE},
+ { .name = "nflog-threshold", .has_arg = true, .val = NFLOG_THRESHOLD},
+ { .name = "nflog", .has_arg = false, .val = NFLOG_NFLOG},
+ XT_GETOPT_TABLEEND,
+};
+
+static void brnflog_help(void)
+{
+ printf("nflog options:\n"
+ "--nflog : use the default nflog parameters\n"
+ "--nflog-prefix prefix : Prefix string for log message\n"
+ "--nflog-group group : NETLINK group used for logging\n"
+ "--nflog-range range : Number of byte to copy\n"
+ "--nflog-threshold : Message threshold of"
+ "in-kernel queue\n");
+}
+
+static void brnflog_init(struct xt_entry_target *t)
+{
+ struct ebt_nflog_info *info = (struct ebt_nflog_info *)t->data;
+
+ info->prefix[0] = '\0';
+ info->group = EBT_NFLOG_DEFAULT_GROUP;
+ info->threshold = EBT_NFLOG_DEFAULT_THRESHOLD;
+}
+
+static int brnflog_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **target)
+{
+ struct ebt_nflog_info *info = (struct ebt_nflog_info *)(*target)->data;
+ unsigned int i;
+
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "The use of '!' makes no sense for the"
+ " nflog watcher");
+
+ switch (c) {
+ case NFLOG_PREFIX:
+ EBT_CHECK_OPTION(flags, NFLOG_PREFIX);
+ if (strlen(optarg) > EBT_NFLOG_PREFIX_SIZE - 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "Prefix too long for nflog-prefix");
+ strncpy(info->prefix, optarg, EBT_NFLOG_PREFIX_SIZE);
+ break;
+ case NFLOG_GROUP:
+ EBT_CHECK_OPTION(flags, NFLOG_GROUP);
+ if (!xtables_strtoui(optarg, NULL, &i, 1, UINT32_MAX))
+ xtables_error(PARAMETER_PROBLEM,
+ "--nflog-group must be a number!");
+ info->group = i;
+ break;
+ case NFLOG_RANGE:
+ EBT_CHECK_OPTION(flags, NFLOG_RANGE);
+ if (!xtables_strtoui(optarg, NULL, &i, 1, UINT32_MAX))
+ xtables_error(PARAMETER_PROBLEM,
+ "--nflog-range must be a number!");
+ info->len = i;
+ break;
+ case NFLOG_THRESHOLD:
+ EBT_CHECK_OPTION(flags, NFLOG_THRESHOLD);
+ if (!xtables_strtoui(optarg, NULL, &i, 1, UINT32_MAX))
+ xtables_error(PARAMETER_PROBLEM,
+ "--nflog-threshold must be a number!");
+ info->threshold = i;
+ break;
+ case NFLOG_NFLOG:
+ EBT_CHECK_OPTION(flags, NFLOG_NFLOG);
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static void
+brnflog_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ struct ebt_nflog_info *info = (struct ebt_nflog_info *)target->data;
+
+ if (info->prefix[0] != '\0')
+ printf("--nflog-prefix \"%s\" ", info->prefix);
+ if (info->group)
+ printf("--nflog-group %d ", info->group);
+ if (info->len)
+ printf("--nflog-range %d ", info->len);
+ if (info->threshold != EBT_NFLOG_DEFAULT_THRESHOLD)
+ printf("--nflog-threshold %d ", info->threshold);
+}
+
+static struct xtables_target brnflog_watcher = {
+ .name = "nflog",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_BRIDGE,
+ .size = XT_ALIGN(sizeof(struct ebt_nflog_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct ebt_nflog_info)),
+ .init = brnflog_init,
+ .help = brnflog_help,
+ .parse = brnflog_parse,
+ .print = brnflog_print,
+ .extra_opts = brnflog_opts,
+};
+
+void _init(void)
+{
+ xtables_register_target(&brnflog_watcher);
+}
diff --git a/extensions/libip6t_DNAT.c b/extensions/libip6t_DNAT.c
index eaa6bf1e..08d920db 100644
--- a/extensions/libip6t_DNAT.c
+++ b/extensions/libip6t_DNAT.c
@@ -231,6 +231,56 @@ static void DNAT_save(const void *ip, const struct xt_entry_target *target)
printf(" --persistent");
}
+static void print_range_xlate(const struct nf_nat_range *range,
+ struct xt_xlate *xl)
+{
+ bool proto_specified = range->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
+
+ if (range->flags & NF_NAT_RANGE_MAP_IPS) {
+ xt_xlate_add(xl, "%s%s%s",
+ proto_specified ? "[" : "",
+ xtables_ip6addr_to_numeric(&range->min_addr.in6),
+ proto_specified ? "]" : "");
+
+ if (memcmp(&range->min_addr, &range->max_addr,
+ sizeof(range->min_addr))) {
+ xt_xlate_add(xl, "-%s%s%s",
+ proto_specified ? "[" : "",
+ xtables_ip6addr_to_numeric(&range->max_addr.in6),
+ proto_specified ? "]" : "");
+ }
+ }
+ if (proto_specified) {
+ xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
+
+ if (range->max_proto.tcp.port != range->min_proto.tcp.port)
+ xt_xlate_add(xl, "-%hu",
+ ntohs(range->max_proto.tcp.port));
+ }
+}
+
+static int DNAT_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct nf_nat_range *range = (const void *)params->target->data;
+ bool sep_need = false;
+ const char *sep = " ";
+
+ xt_xlate_add(xl, "dnat to ");
+ print_range_xlate(range, xl);
+ if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
+ xt_xlate_add(xl, " random");
+ sep_need = true;
+ }
+ if (range->flags & NF_NAT_RANGE_PERSISTENT) {
+ if (sep_need)
+ sep = ",";
+ xt_xlate_add(xl, "%spersistent", sep);
+ }
+
+ return 1;
+}
+
static struct xtables_target snat_tg_reg = {
.name = "DNAT",
.version = XTABLES_VERSION,
@@ -244,6 +294,7 @@ static struct xtables_target snat_tg_reg = {
.print = DNAT_print,
.save = DNAT_save,
.x6_options = DNAT_opts,
+ .xlate = DNAT_xlate,
};
void _init(void)
diff --git a/extensions/libip6t_DNAT.t b/extensions/libip6t_DNAT.t
new file mode 100644
index 00000000..3141c299
--- /dev/null
+++ b/extensions/libip6t_DNAT.t
@@ -0,0 +1,8 @@
+:PREROUTING
+*nat
+-j DNAT --to-destination dead::beef;=;OK
+-j DNAT --to-destination dead::beef-dead::fee7;=;OK
+-p tcp -j DNAT --to-destination [dead::beef]:1025-65535;=;OK
+-p tcp -j DNAT --to-destination [dead::beef-dead::fee7]:1025-65535;=;OK
+-p tcp -j DNAT --to-destination [dead::beef-dead::fee7]:1025-65536;;FAIL
+-j DNAT;;FAIL
diff --git a/extensions/libip6t_DNPT.c b/extensions/libip6t_DNPT.c
index a442de6d..d045e44c 100644
--- a/extensions/libip6t_DNPT.c
+++ b/extensions/libip6t_DNPT.c
@@ -52,9 +52,9 @@ static void DNPT_print(const void *ip, const struct xt_entry_target *target,
{
const struct ip6t_npt_tginfo *npt = (const void *)target->data;
- printf("src-pfx %s/%u ", xtables_ip6addr_to_numeric(&npt->src_pfx.in6),
+ printf(" DNPT src-pfx %s/%u", xtables_ip6addr_to_numeric(&npt->src_pfx.in6),
npt->src_pfx_len);
- printf("dst-pfx %s/%u ", xtables_ip6addr_to_numeric(&npt->dst_pfx.in6),
+ printf(" dst-pfx %s/%u", xtables_ip6addr_to_numeric(&npt->dst_pfx.in6),
npt->dst_pfx_len);
}
@@ -65,12 +65,12 @@ static void DNPT_save(const void *ip, const struct xt_entry_target *target)
if (memcmp(&info->src_pfx.in6, &zero_addr, sizeof(zero_addr)) != 0 ||
info->src_pfx_len != 0)
- printf("--src-pfx %s/%u ",
+ printf(" --src-pfx %s/%u",
xtables_ip6addr_to_numeric(&info->src_pfx.in6),
info->src_pfx_len);
if (memcmp(&info->dst_pfx.in6, &zero_addr, sizeof(zero_addr)) != 0 ||
info->dst_pfx_len != 0)
- printf("--dst-pfx %s/%u ",
+ printf(" --dst-pfx %s/%u",
xtables_ip6addr_to_numeric(&info->dst_pfx.in6),
info->dst_pfx_len);
}
diff --git a/extensions/libip6t_DNPT.t b/extensions/libip6t_DNPT.t
new file mode 100644
index 00000000..0406dc90
--- /dev/null
+++ b/extensions/libip6t_DNPT.t
@@ -0,0 +1,7 @@
+:PREROUTING
+*mangle
+-j DNPT --src-pfx dead::/64 --dst-pfx 1c3::/64;=;OK
+-j DNPT --src-pfx dead::beef --dst-pfx 1c3::/64;;FAIL
+-j DNPT --src-pfx dead::/64;;FAIL
+-j DNPT --dst-pfx dead::/64;;FAIL
+-j DNPT;;FAIL
diff --git a/extensions/libip6t_HL.t b/extensions/libip6t_HL.t
new file mode 100644
index 00000000..4e529f88
--- /dev/null
+++ b/extensions/libip6t_HL.t
@@ -0,0 +1,10 @@
+:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j HL --hl-set 42;=;OK
+-j HL --hl-inc 1;=;OK
+-j HL --hl-dec 1;=;OK
+-j HL --hl-set 256;;FAIL
+-j HL --hl-inc 0;;FAIL
+-j HL --hl-dec 0;;FAIL
+-j HL --hl-dec 1 --hl-inc 1;;FAIL
+-j HL --hl-set --hl-inc 1;;FAIL
diff --git a/extensions/libip6t_LOG.c b/extensions/libip6t_LOG.c
index 4639268d..40adc69d 100644
--- a/extensions/libip6t_LOG.c
+++ b/extensions/libip6t_LOG.c
@@ -63,6 +63,11 @@ struct ip6t_log_names {
unsigned int level;
};
+struct ip6t_log_xlate {
+ const char *name;
+ unsigned int level;
+};
+
static const struct ip6t_log_names ip6t_log_names[]
= { { .name = "alert", .level = LOG_ALERT },
{ .name = "crit", .level = LOG_CRIT },
@@ -166,6 +171,64 @@ static void LOG_save(const void *ip, const struct xt_entry_target *target)
printf(" --log-macdecode");
}
+static const struct ip6t_log_xlate ip6t_log_xlate_names[] = {
+ {"alert", LOG_ALERT },
+ {"crit", LOG_CRIT },
+ {"debug", LOG_DEBUG },
+ {"emerg", LOG_EMERG },
+ {"err", LOG_ERR },
+ {"info", LOG_INFO },
+ {"notice", LOG_NOTICE },
+ {"warn", LOG_WARNING }
+};
+
+static int LOG_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct ip6t_log_info *loginfo =
+ (const struct ip6t_log_info *)params->target->data;
+ unsigned int i = 0;
+
+ xt_xlate_add(xl, "log");
+ if (strcmp(loginfo->prefix, "") != 0) {
+ if (params->escape_quotes)
+ xt_xlate_add(xl, " prefix \\\"%s\\\"", loginfo->prefix);
+ else
+ xt_xlate_add(xl, " prefix \"%s\"", loginfo->prefix);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ip6t_log_xlate_names); ++i)
+ if (loginfo->level == ip6t_log_xlate_names[i].level &&
+ loginfo->level != LOG_DEFAULT_LEVEL) {
+ xt_xlate_add(xl, " level %s",
+ ip6t_log_xlate_names[i].name);
+ break;
+ }
+
+ if ((loginfo->logflags & IP6T_LOG_MASK) == IP6T_LOG_MASK) {
+ xt_xlate_add(xl, " flags all");
+ } else {
+ if (loginfo->logflags & (IP6T_LOG_TCPSEQ | IP6T_LOG_TCPOPT)) {
+ const char *delim = " ";
+
+ xt_xlate_add(xl, " flags tcp");
+ if (loginfo->logflags & IP6T_LOG_TCPSEQ) {
+ xt_xlate_add(xl, " sequence");
+ delim = ",";
+ }
+ if (loginfo->logflags & IP6T_LOG_TCPOPT)
+ xt_xlate_add(xl, "%soptions", delim);
+ }
+ if (loginfo->logflags & IP6T_LOG_IPOPT)
+ xt_xlate_add(xl, " flags ip options");
+ if (loginfo->logflags & IP6T_LOG_UID)
+ xt_xlate_add(xl, " flags skuid");
+ if (loginfo->logflags & IP6T_LOG_MACDECODE)
+ xt_xlate_add(xl, " flags ether");
+ }
+
+ return 1;
+}
static struct xtables_target log_tg6_reg = {
.name = "LOG",
.version = XTABLES_VERSION,
@@ -178,6 +241,7 @@ static struct xtables_target log_tg6_reg = {
.save = LOG_save,
.x6_parse = LOG_parse,
.x6_options = LOG_opts,
+ .xlate = LOG_xlate,
};
void _init(void)
diff --git a/extensions/libip6t_LOG.t b/extensions/libip6t_LOG.t
new file mode 100644
index 00000000..fbf5118b
--- /dev/null
+++ b/extensions/libip6t_LOG.t
@@ -0,0 +1,12 @@
+:INPUT,FORWARD,OUTPUT
+-j LOG;-j LOG;OK
+-j LOG --log-prefix "test: ";=;OK
+-j LOG --log-prefix "test: " --log-level 1;=;OK
+# iptables displays the log-level output using the number; not the string
+-j LOG --log-prefix "test: " --log-level alert;-j LOG --log-prefix "test: " --log-level 1;OK
+-j LOG --log-prefix "test: " --log-tcp-sequence;=;OK
+-j LOG --log-prefix "test: " --log-tcp-options;=;OK
+-j LOG --log-prefix "test: " --log-ip-options;=;OK
+-j LOG --log-prefix "test: " --log-uid;=;OK
+-j LOG --log-prefix "test: " --log-level bad;;FAIL
+-j LOG --log-prefix;;FAIL
diff --git a/extensions/libip6t_MASQUERADE.c b/extensions/libip6t_MASQUERADE.c
index eb9213ef..3b59e43e 100644
--- a/extensions/libip6t_MASQUERADE.c
+++ b/extensions/libip6t_MASQUERADE.c
@@ -131,6 +131,26 @@ MASQUERADE_save(const void *ip, const struct xt_entry_target *target)
printf(" --random");
}
+static int MASQUERADE_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct nf_nat_range *r = (const void *)params->target->data;
+
+ xt_xlate_add(xl, "masquerade");
+
+ if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+ xt_xlate_add(xl, " to :%hu", ntohs(r->min_proto.tcp.port));
+ if (r->max_proto.tcp.port != r->min_proto.tcp.port)
+ xt_xlate_add(xl, "-%hu", ntohs(r->max_proto.tcp.port));
+ }
+
+ xt_xlate_add(xl, " ");
+ if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
+ xt_xlate_add(xl, "random ");
+
+ return 1;
+}
+
static struct xtables_target masquerade_tg_reg = {
.name = "MASQUERADE",
.version = XTABLES_VERSION,
@@ -142,6 +162,7 @@ static struct xtables_target masquerade_tg_reg = {
.print = MASQUERADE_print,
.save = MASQUERADE_save,
.x6_options = MASQUERADE_opts,
+ .xlate = MASQUERADE_xlate,
};
void _init(void)
diff --git a/extensions/libip6t_MASQUERADE.t b/extensions/libip6t_MASQUERADE.t
new file mode 100644
index 00000000..46502040
--- /dev/null
+++ b/extensions/libip6t_MASQUERADE.t
@@ -0,0 +1,8 @@
+:POSTROUTING
+*nat
+-j MASQUERADE;=;OK
+-j MASQUERADE --random;=;OK
+-p tcp -j MASQUERADE --to-ports 1024;=;OK
+-p udp -j MASQUERADE --to-ports 1024-65535;=;OK
+-p udp -j MASQUERADE --to-ports 1024-65536;;FAIL
+-p udp -j MASQUERADE --to-ports -1;;FAIL
diff --git a/extensions/libip6t_NETMAP.c b/extensions/libip6t_NETMAP.c
index a4df70ee..579ed04e 100644
--- a/extensions/libip6t_NETMAP.c
+++ b/extensions/libip6t_NETMAP.c
@@ -49,8 +49,8 @@ static void NETMAP_parse(struct xt_option_call *cb)
}
}
-static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
- int numeric)
+static void __NETMAP_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
{
const struct nf_nat_range *r = (const void *)target->data;
struct in6_addr a;
@@ -68,10 +68,17 @@ static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
printf("/%d", bits);
}
+static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ printf(" to:");
+ __NETMAP_print(ip, target, numeric);
+}
+
static void NETMAP_save(const void *ip, const struct xt_entry_target *target)
{
printf(" --%s ", NETMAP_opts[0].name);
- NETMAP_print(ip, target, 0);
+ __NETMAP_print(ip, target, 0);
}
static struct xtables_target netmap_tg_reg = {
diff --git a/extensions/libip6t_NETMAP.t b/extensions/libip6t_NETMAP.t
new file mode 100644
index 00000000..043562d2
--- /dev/null
+++ b/extensions/libip6t_NETMAP.t
@@ -0,0 +1,4 @@
+:PREROUTING,INPUT,OUTPUT,POSTROUTING
+*nat
+-j NETMAP --to dead::/64;=;OK
+-j NETMAP --to dead::beef;=;OK
diff --git a/extensions/libip6t_REDIRECT.c b/extensions/libip6t_REDIRECT.c
index 1724aa67..8e04d2cd 100644
--- a/extensions/libip6t_REDIRECT.c
+++ b/extensions/libip6t_REDIRECT.c
@@ -132,6 +132,24 @@ static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
}
}
+static int REDIRECT_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct nf_nat_range *range = (const void *)params->target->data;
+
+ if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+ xt_xlate_add(xl, "redirect to :%hu",
+ ntohs(range->min_proto.tcp.port));
+ if (range->max_proto.tcp.port != range->min_proto.tcp.port)
+ xt_xlate_add(xl, "-%hu ",
+ ntohs(range->max_proto.tcp.port));
+ if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+ xt_xlate_add(xl, " random ");
+ }
+
+ return 1;
+}
+
static struct xtables_target redirect_tg_reg = {
.name = "REDIRECT",
.version = XTABLES_VERSION,
@@ -143,6 +161,7 @@ static struct xtables_target redirect_tg_reg = {
.print = REDIRECT_print,
.save = REDIRECT_save,
.x6_options = REDIRECT_opts,
+ .xlate = REDIRECT_xlate,
};
void _init(void)
diff --git a/extensions/libip6t_REDIRECT.t b/extensions/libip6t_REDIRECT.t
new file mode 100644
index 00000000..a0fb0ed1
--- /dev/null
+++ b/extensions/libip6t_REDIRECT.t
@@ -0,0 +1,6 @@
+:PREROUTING,OUTPUT
+*nat
+-p tcp -j REDIRECT --to-ports 42;=;OK
+-p udp -j REDIRECT --to-ports 42-1234;=;OK
+-p tcp -j REDIRECT --to-ports 42-1234 --random;=;OK
+-j REDIRECT --to-ports 42;;FAIL
diff --git a/extensions/libip6t_REJECT.c b/extensions/libip6t_REJECT.c
index 8085321a..c5b980d0 100644
--- a/extensions/libip6t_REJECT.c
+++ b/extensions/libip6t_REJECT.c
@@ -17,6 +17,11 @@ struct reject_names {
const char *desc;
};
+struct reject_names_xlate {
+ const char *name;
+ enum ip6t_reject_with with;
+};
+
enum {
O_REJECT_WITH = 0,
};
@@ -35,7 +40,11 @@ static const struct reject_names reject_table[] = {
{"icmp6-port-unreachable", "port-unreach",
IP6T_ICMP6_PORT_UNREACH, "ICMPv6 port unreachable"},
{"tcp-reset", "tcp-reset",
- IP6T_TCP_RESET, "TCP RST packet"}
+ IP6T_TCP_RESET, "TCP RST packet"},
+ {"icmp6-policy-fail", "policy-fail",
+ IP6T_ICMP6_POLICY_FAIL, "ICMPv6 policy fail"},
+ {"icmp6-reject-route", "reject-route",
+ IP6T_ICMP6_REJECT_ROUTE, "ICMPv6 reject route"}
};
static void
@@ -120,6 +129,38 @@ static void REJECT_save(const void *ip, const struct xt_entry_target *target)
printf(" --reject-with %s", reject_table[i].name);
}
+static const struct reject_names_xlate reject_table_xlate[] = {
+ {"no-route", IP6T_ICMP6_NO_ROUTE},
+ {"admin-prohibited", IP6T_ICMP6_ADM_PROHIBITED},
+ {"addr-unreachable", IP6T_ICMP6_ADDR_UNREACH},
+ {"port-unreachable", IP6T_ICMP6_PORT_UNREACH},
+ {"tcp reset", IP6T_TCP_RESET},
+ {"policy-fail", IP6T_ICMP6_POLICY_FAIL},
+ {"reject-route", IP6T_ICMP6_REJECT_ROUTE}
+};
+
+static int REJECT_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct ip6t_reject_info *reject =
+ (const struct ip6t_reject_info *)params->target->data;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(reject_table_xlate); ++i)
+ if (reject_table_xlate[i].with == reject->with)
+ break;
+
+ if (reject->with == IP6T_ICMP6_PORT_UNREACH)
+ xt_xlate_add(xl, "reject");
+ else if (reject->with == IP6T_TCP_RESET)
+ xt_xlate_add(xl, "reject with %s", reject_table_xlate[i].name);
+ else
+ xt_xlate_add(xl, "reject with icmpv6 type %s",
+ reject_table_xlate[i].name);
+
+ return 1;
+}
+
static struct xtables_target reject_tg6_reg = {
.name = "REJECT",
.version = XTABLES_VERSION,
@@ -132,6 +173,7 @@ static struct xtables_target reject_tg6_reg = {
.save = REJECT_save,
.x6_parse = REJECT_parse,
.x6_options = REJECT_opts,
+ .xlate = REJECT_xlate,
};
void _init(void)
diff --git a/extensions/libip6t_REJECT.man b/extensions/libip6t_REJECT.man
index 2d09e050..0030a51f 100644
--- a/extensions/libip6t_REJECT.man
+++ b/extensions/libip6t_REJECT.man
@@ -18,10 +18,9 @@ The type given can be
\fBicmp6\-adm\-prohibited\fP,
\fBadm\-prohibited\fP,
\fBicmp6\-addr\-unreachable\fP,
-\fBaddr\-unreach\fP,
-\fBicmp6\-port\-unreachable\fP or
-\fBport\-unreach\fP
-which return the appropriate ICMPv6 error message (\fBport\-unreach\fP is
+\fBaddr\-unreach\fP, or
+\fBicmp6\-port\-unreachable\fP,
+which return the appropriate ICMPv6 error message (\fBicmp6\-port\-unreachable\fP is
the default). Finally, the option
\fBtcp\-reset\fP
can be used on rules which only match the TCP protocol: this causes a
diff --git a/extensions/libip6t_REJECT.t b/extensions/libip6t_REJECT.t
new file mode 100644
index 00000000..d2b337d7
--- /dev/null
+++ b/extensions/libip6t_REJECT.t
@@ -0,0 +1,11 @@
+:INPUT,FORWARD,OUTPUT
+-j REJECT;=;OK
+# manpage for IPv6 variant of REJECT does not show up for some reason?
+-j REJECT --reject-with icmp6-no-route;=;OK
+-j REJECT --reject-with icmp6-adm-prohibited;=;OK
+-j REJECT --reject-with icmp6-addr-unreachable;=;OK
+-j REJECT --reject-with icmp6-port-unreachable;=;OK
+-j REJECT --reject-with icmp6-policy-fail;=;OK
+-j REJECT --reject-with icmp6-reject-route;=;OK
+-p tcp -j REJECT --reject-with tcp-reset;=;OK
+-j REJECT --reject-with tcp-reset;;FAIL
diff --git a/extensions/libip6t_SNAT.c b/extensions/libip6t_SNAT.c
index 7382ad06..671ac61a 100644
--- a/extensions/libip6t_SNAT.c
+++ b/extensions/libip6t_SNAT.c
@@ -18,11 +18,13 @@
enum {
O_TO_SRC = 0,
O_RANDOM,
+ O_RANDOM_FULLY,
O_PERSISTENT,
O_X_TO_SRC,
- F_TO_SRC = 1 << O_TO_SRC,
- F_RANDOM = 1 << O_RANDOM,
- F_X_TO_SRC = 1 << O_X_TO_SRC,
+ F_TO_SRC = 1 << O_TO_SRC,
+ F_RANDOM = 1 << O_RANDOM,
+ F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
+ F_X_TO_SRC = 1 << O_X_TO_SRC,
};
static void SNAT_help(void)
@@ -31,13 +33,14 @@ static void SNAT_help(void)
"SNAT target options:\n"
" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
" Address to map source to.\n"
-"[--random] [--persistent]\n");
+"[--random] [--random-fully] [--persistent]\n");
}
static const struct xt_option_entry SNAT_opts[] = {
{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_MULTI},
{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+ {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
@@ -180,10 +183,13 @@ static void SNAT_parse(struct xt_option_call *cb)
static void SNAT_fcheck(struct xt_fcheck_call *cb)
{
static const unsigned int f = F_TO_SRC | F_RANDOM;
+ static const unsigned int r = F_TO_SRC | F_RANDOM_FULLY;
struct nf_nat_range *range = cb->data;
if ((cb->xflags & f) == f)
range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
+ if ((cb->xflags & r) == r)
+ range->flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
}
static void print_range(const struct nf_nat_range *range)
@@ -215,6 +221,8 @@ static void SNAT_print(const void *ip, const struct xt_entry_target *target,
print_range(range);
if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
printf(" random");
+ if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
+ printf(" random-fully");
if (range->flags & NF_NAT_RANGE_PERSISTENT)
printf(" persistent");
}
@@ -227,10 +235,68 @@ static void SNAT_save(const void *ip, const struct xt_entry_target *target)
print_range(range);
if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
printf(" --random");
+ if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
+ printf(" --random-fully");
if (range->flags & NF_NAT_RANGE_PERSISTENT)
printf(" --persistent");
}
+static void print_range_xlate(const struct nf_nat_range *range,
+ struct xt_xlate *xl)
+{
+ bool proto_specified = range->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
+
+ if (range->flags & NF_NAT_RANGE_MAP_IPS) {
+ xt_xlate_add(xl, "%s%s%s",
+ proto_specified ? "[" : "",
+ xtables_ip6addr_to_numeric(&range->min_addr.in6),
+ proto_specified ? "]" : "");
+
+ if (memcmp(&range->min_addr, &range->max_addr,
+ sizeof(range->min_addr))) {
+ xt_xlate_add(xl, "-%s%s%s",
+ proto_specified ? "[" : "",
+ xtables_ip6addr_to_numeric(&range->max_addr.in6),
+ proto_specified ? "]" : "");
+ }
+ }
+ if (proto_specified) {
+ xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
+
+ if (range->max_proto.tcp.port != range->min_proto.tcp.port)
+ xt_xlate_add(xl, "-%hu",
+ ntohs(range->max_proto.tcp.port));
+ }
+}
+
+static int SNAT_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct nf_nat_range *range = (const void *)params->target->data;
+ bool sep_need = false;
+ const char *sep = " ";
+
+ xt_xlate_add(xl, "snat to ");
+ print_range_xlate(range, xl);
+ if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
+ xt_xlate_add(xl, " random");
+ sep_need = true;
+ }
+ if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
+ if (sep_need)
+ sep = ",";
+ xt_xlate_add(xl, "%sfully-random", sep);
+ sep_need = true;
+ }
+ if (range->flags & NF_NAT_RANGE_PERSISTENT) {
+ if (sep_need)
+ sep = ",";
+ xt_xlate_add(xl, "%spersistent", sep);
+ }
+
+ return 1;
+}
+
static struct xtables_target snat_tg_reg = {
.name = "SNAT",
.version = XTABLES_VERSION,
@@ -244,6 +310,7 @@ static struct xtables_target snat_tg_reg = {
.print = SNAT_print,
.save = SNAT_save,
.x6_options = SNAT_opts,
+ .xlate = SNAT_xlate,
};
void _init(void)
diff --git a/extensions/libip6t_SNAT.t b/extensions/libip6t_SNAT.t
new file mode 100644
index 00000000..bb080497
--- /dev/null
+++ b/extensions/libip6t_SNAT.t
@@ -0,0 +1,8 @@
+:POSTROUTING
+*nat
+-j SNAT --to-source dead::beef;=;OK
+-j SNAT --to-source dead::beef-dead::fee7;=;OK
+-p tcp -j SNAT --to-source [dead::beef]:1025-65535;=;OK
+-p tcp -j SNAT --to-source [dead::beef-dead::fee7]:1025-65535;=;OK
+-p tcp -j SNAT --to-source [dead::beef-dead::fee7]:1025-65536;;FAIL
+-j SNAT;;FAIL
diff --git a/extensions/libip6t_SNPT.c b/extensions/libip6t_SNPT.c
index 4f10de03..65f787d9 100644
--- a/extensions/libip6t_SNPT.c
+++ b/extensions/libip6t_SNPT.c
@@ -52,9 +52,9 @@ static void SNPT_print(const void *ip, const struct xt_entry_target *target,
{
const struct ip6t_npt_tginfo *npt = (const void *)target->data;
- printf("src-pfx %s/%u ", xtables_ip6addr_to_numeric(&npt->src_pfx.in6),
+ printf(" SNPT src-pfx %s/%u", xtables_ip6addr_to_numeric(&npt->src_pfx.in6),
npt->src_pfx_len);
- printf("dst-pfx %s/%u ", xtables_ip6addr_to_numeric(&npt->dst_pfx.in6),
+ printf(" dst-pfx %s/%u", xtables_ip6addr_to_numeric(&npt->dst_pfx.in6),
npt->dst_pfx_len);
}
@@ -65,12 +65,12 @@ static void SNPT_save(const void *ip, const struct xt_entry_target *target)
if (memcmp(&info->src_pfx.in6, &zero_addr, sizeof(zero_addr)) != 0 ||
info->src_pfx_len != 0)
- printf("--src-pfx %s/%u ",
+ printf(" --src-pfx %s/%u",
xtables_ip6addr_to_numeric(&info->src_pfx.in6),
info->src_pfx_len);
if (memcmp(&info->dst_pfx.in6, &zero_addr, sizeof(zero_addr)) != 0 ||
info->dst_pfx_len != 0)
- printf("--dst-pfx %s/%u ",
+ printf(" --dst-pfx %s/%u",
xtables_ip6addr_to_numeric(&info->dst_pfx.in6),
info->dst_pfx_len);
}
diff --git a/extensions/libip6t_SNPT.t b/extensions/libip6t_SNPT.t
new file mode 100644
index 00000000..7ed6d0c9
--- /dev/null
+++ b/extensions/libip6t_SNPT.t
@@ -0,0 +1,7 @@
+:INPUT,POSTROUTING
+*mangle
+-j SNPT --src-pfx dead::/64 --dst-pfx 1c3::/64;=;OK
+-j SNPT --src-pfx dead::beef --dst-pfx 1c3::/64;;FAIL
+-j SNPT --src-pfx dead::/64;;FAIL
+-j SNPT --dst-pfx dead::/64;;FAIL
+-j SNPT;;FAIL
diff --git a/extensions/libip6t_ah.c b/extensions/libip6t_ah.c
index 26f81408..f35982f3 100644
--- a/extensions/libip6t_ah.c
+++ b/extensions/libip6t_ah.c
@@ -28,6 +28,14 @@ static const struct xt_option_entry ah_opts[] = {
};
#undef s
+static void ah_init(struct xt_entry_match *m)
+{
+ struct ip6t_ah *ahinfo = (void *)m->data;
+
+ /* Defaults for when no --ahspi is used at all */
+ ahinfo->spis[1] = ~0U;
+}
+
static void ah_parse(struct xt_option_call *cb)
{
struct ip6t_ah *ahinfo = cb->data;
@@ -120,6 +128,41 @@ static void ah_save(const void *ip, const struct xt_entry_match *match)
printf(" --ahres");
}
+static int ah_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ip6t_ah *ahinfo = (struct ip6t_ah *)params->match->data;
+ char *space = "";
+
+ if (!(ahinfo->spis[0] == 0 && ahinfo->spis[1] == 0xFFFFFFFF)) {
+ xt_xlate_add(xl, "ah spi%s ",
+ (ahinfo->invflags & IP6T_AH_INV_SPI) ? " !=" : "");
+ if (ahinfo->spis[0] != ahinfo->spis[1])
+ xt_xlate_add(xl, "%u-%u", ahinfo->spis[0],
+ ahinfo->spis[1]);
+ else
+ xt_xlate_add(xl, "%u", ahinfo->spis[0]);
+ space = " ";
+ }
+
+ if (ahinfo->hdrlen != 0 || (ahinfo->invflags & IP6T_AH_INV_LEN)) {
+ xt_xlate_add(xl, "%sah hdrlength%s %u", space,
+ (ahinfo->invflags & IP6T_AH_INV_LEN) ? " !=" : "",
+ ahinfo->hdrlen);
+ space = " ";
+ }
+
+ if (ahinfo->hdrres != 0) {
+ xt_xlate_add(xl, "%sah reserved %u", space, ahinfo->hdrres);
+ space = " ";
+ }
+
+ if (!space[0]) /* plain '-m ah' */
+ xt_xlate_add(xl, "meta l4proto ah");
+
+ return 1;
+}
+
static struct xtables_match ah_mt6_reg = {
.name = "ah",
.version = XTABLES_VERSION,
@@ -127,10 +170,12 @@ static struct xtables_match ah_mt6_reg = {
.size = XT_ALIGN(sizeof(struct ip6t_ah)),
.userspacesize = XT_ALIGN(sizeof(struct ip6t_ah)),
.help = ah_help,
+ .init = ah_init,
.print = ah_print,
.save = ah_save,
.x6_parse = ah_parse,
.x6_options = ah_opts,
+ .xlate = ah_xlate,
};
void
diff --git a/extensions/libip6t_ah.t b/extensions/libip6t_ah.t
new file mode 100644
index 00000000..c1898d44
--- /dev/null
+++ b/extensions/libip6t_ah.t
@@ -0,0 +1,15 @@
+:INPUT,FORWARD,OUTPUT
+-m ah --ahspi 0;=;OK
+-m ah --ahspi 4294967295;=;OK
+-m ah --ahspi 0:4294967295;-m ah;OK
+-m ah ! --ahspi 0;=;OK
+# ERROR: should fail: iptables -A FORWARD -t mangle -j CLASSIFY --set-class 1:-1
+# -m ah --ahres;=;OK
+# ERROR: line 7 (cannot find: ip6tables -I INPUT -m ah --ahlen 32
+# -m ah --ahlen 32;=;OK
+-m ah --ahspi -1;;FAIL
+-m ah --ahspi 4294967296;;FAIL
+-m ah --ahspi invalid;;FAIL
+-m ah --ahspi 0:invalid;;FAIL
+-m ah --ahspi;;FAIL
+-m ah;=;OK
diff --git a/extensions/libip6t_dst.c b/extensions/libip6t_dst.c
index 3fd4c019..fe7e3403 100644
--- a/extensions/libip6t_dst.c
+++ b/extensions/libip6t_dst.c
@@ -112,6 +112,8 @@ static void dst_parse(struct xt_option_call *cb)
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_DSTLEN:
+ if (cb->invert)
+ optinfo->invflags |= IP6T_OPTS_INV_LEN;
optinfo->flags |= IP6T_OPTS_LEN;
break;
case O_DSTOPTS:
diff --git a/extensions/libip6t_dst.t b/extensions/libip6t_dst.t
new file mode 100644
index 00000000..0b0013b5
--- /dev/null
+++ b/extensions/libip6t_dst.t
@@ -0,0 +1,5 @@
+:INPUT,FORWARD,OUTPUT
+-m dst --dst-len 0;=;OK
+-m dst --dst-opts 149:92,12:12,123:12;=;OK
+-m dst ! --dst-len 42;=;OK
+-m dst --dst-len 42 --dst-opts 149:92,12:12,123:12;=;OK
diff --git a/extensions/libip6t_eui64.t b/extensions/libip6t_eui64.t
new file mode 100644
index 00000000..e5aaaace
--- /dev/null
+++ b/extensions/libip6t_eui64.t
@@ -0,0 +1,8 @@
+:PREROUTING
+*raw
+-m eui64;=;OK
+:INPUT,FORWARD
+*filter
+-m eui64;=;OK
+:OUTPUT
+-m eui64;;FAIL
diff --git a/extensions/libip6t_frag.c b/extensions/libip6t_frag.c
index 023df627..3842496e 100644
--- a/extensions/libip6t_frag.c
+++ b/extensions/libip6t_frag.c
@@ -173,6 +173,45 @@ static void frag_save(const void *ip, const struct xt_entry_match *match)
printf(" --fraglast");
}
+static int frag_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ip6t_frag *fraginfo =
+ (struct ip6t_frag *)params->match->data;
+ char *space= "";
+
+ if (!(fraginfo->ids[0] == 0 && fraginfo->ids[1] == 0xFFFFFFFF)) {
+ xt_xlate_add(xl, "frag id %s",
+ (fraginfo->invflags & IP6T_FRAG_INV_IDS) ?
+ "!= " : "");
+ if (fraginfo->ids[0] != fraginfo->ids[1])
+ xt_xlate_add(xl, "%u-%u", fraginfo->ids[0],
+ fraginfo->ids[1]);
+ else
+ xt_xlate_add(xl, "%u", fraginfo->ids[0]);
+
+ space = " ";
+ }
+
+ if (fraginfo->flags & IP6T_FRAG_RES) {
+ xt_xlate_add(xl, "%sfrag reserved 1", space);
+ space = " ";
+ }
+ if (fraginfo->flags & IP6T_FRAG_FST) {
+ xt_xlate_add(xl, "%sfrag frag-off 0", space);
+ space = " ";
+ }
+ if (fraginfo->flags & IP6T_FRAG_MF) {
+ xt_xlate_add(xl, "%sfrag more-fragments 1", space);
+ space = " ";
+ }
+ if (fraginfo->flags & IP6T_FRAG_NMF) {
+ xt_xlate_add(xl, "%sfrag more-fragments 0", space);
+ }
+
+ return 1;
+}
+
static struct xtables_match frag_mt6_reg = {
.name = "frag",
.version = XTABLES_VERSION,
@@ -185,6 +224,7 @@ static struct xtables_match frag_mt6_reg = {
.save = frag_save,
.x6_parse = frag_parse,
.x6_options = frag_opts,
+ .xlate = frag_xlate,
};
void
diff --git a/extensions/libip6t_frag.t b/extensions/libip6t_frag.t
new file mode 100644
index 00000000..dab49894
--- /dev/null
+++ b/extensions/libip6t_frag.t
@@ -0,0 +1,11 @@
+:INPUT,FORWARD,OUTPUT
+-m frag --fragid 1:42;=;OK
+-m frag --fraglen 42;=;OK
+-m frag --fragres;=;OK
+-m frag --fragfirst;=;OK
+-m frag --fragmore;=;OK
+-m frag --fraglast;=;OK
+-m frag ! --fragid 1 ! --fraglen 42 --fragres --fragfirst;=;OK
+-m frag --fragfirst --fragmore;=;OK
+-m frag --fragfirst --fraglast;=;OK
+-m frag --fraglast --fragmore;;FAIL
diff --git a/extensions/libip6t_hbh.c b/extensions/libip6t_hbh.c
index c0389ed8..76b4ff00 100644
--- a/extensions/libip6t_hbh.c
+++ b/extensions/libip6t_hbh.c
@@ -164,6 +164,23 @@ static void hbh_save(const void *ip, const struct xt_entry_match *match)
print_options(optinfo->optsnr, (uint16_t *)optinfo->opts);
}
+static int hbh_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ip6t_opts *optinfo =
+ (struct ip6t_opts *)params->match->data;
+
+ if (!(optinfo->flags & IP6T_OPTS_LEN) ||
+ (optinfo->flags & IP6T_OPTS_OPTS))
+ return 0;
+
+ xt_xlate_add(xl, "hbh hdrlength %s%u",
+ (optinfo->invflags & IP6T_OPTS_INV_LEN) ? "!= " : "",
+ optinfo->hdrlen);
+
+ return 1;
+}
+
static struct xtables_match hbh_mt6_reg = {
.name = "hbh",
.version = XTABLES_VERSION,
@@ -175,6 +192,7 @@ static struct xtables_match hbh_mt6_reg = {
.save = hbh_save,
.x6_parse = hbh_parse,
.x6_options = hbh_opts,
+ .xlate = hbh_xlate,
};
void
diff --git a/extensions/libip6t_hbh.t b/extensions/libip6t_hbh.t
new file mode 100644
index 00000000..4b58f25a
--- /dev/null
+++ b/extensions/libip6t_hbh.t
@@ -0,0 +1,5 @@
+:INPUT,FORWARD,OUTPUT
+-m hbh;=;OK
+-m hbh --hbh-len 42;=;OK
+-m hbh ! --hbh-len 42;=;OK
+-m hbh --hbh-len 42 --hbh-opts 1:2,23:42,4:6,8:10,42,23,4:5;=;OK
diff --git a/extensions/libip6t_hl.c b/extensions/libip6t_hl.c
index 3559db46..37922f6f 100644
--- a/extensions/libip6t_hl.c
+++ b/extensions/libip6t_hl.c
@@ -83,6 +83,24 @@ static void hl_save(const void *ip, const struct xt_entry_match *match)
printf(" %s %u", op[info->mode], info->hop_limit);
}
+static const char *const op[] = {
+ [IP6T_HL_EQ] = "",
+ [IP6T_HL_NE] = "!= ",
+ [IP6T_HL_LT] = "lt ",
+ [IP6T_HL_GT] = "gt "
+};
+
+static int hl_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ip6t_hl_info *info =
+ (struct ip6t_hl_info *) params->match->data;
+
+ xt_xlate_add(xl, "ip6 hoplimit %s%u", op[info->mode], info->hop_limit);
+
+ return 1;
+}
+
#define s struct ip6t_hl_info
static const struct xt_option_entry hl_opts[] = {
{.name = "hl-lt", .id = O_HL_LT, .excl = F_ANY, .type = XTTYPE_UINT8,
@@ -109,6 +127,7 @@ static struct xtables_match hl_mt6_reg = {
.x6_parse = hl_parse,
.x6_fcheck = hl_check,
.x6_options = hl_opts,
+ .xlate = hl_xlate,
};
diff --git a/extensions/libip6t_hl.t b/extensions/libip6t_hl.t
new file mode 100644
index 00000000..b02816af
--- /dev/null
+++ b/extensions/libip6t_hl.t
@@ -0,0 +1,8 @@
+:INPUT,FORWARD,OUTPUT
+-m hl;;FAIL
+-m hl --hl-eq 42;=;OK
+-m hl ! --hl-eq 42;=;OK
+-m hl --hl-lt 42;=;OK
+-m hl --hl-gt 42;=;OK
+-m hl --hl-gt 42 --hl-eq 42;;FAIL
+-m hl --hl-gt;;FAIL
diff --git a/extensions/libip6t_icmp6.c b/extensions/libip6t_icmp6.c
index 68b940bd..b49a241d 100644
--- a/extensions/libip6t_icmp6.c
+++ b/extensions/libip6t_icmp6.c
@@ -4,6 +4,7 @@
#include <xtables.h>
#include <limits.h> /* INT_MAX in ip6_tables.h */
#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <netinet/icmp6.h>
enum {
O_ICMPV6_TYPE = 0,
@@ -19,8 +20,11 @@ static const struct icmpv6_names icmpv6_codes[] = {
{ "destination-unreachable", 1, 0, 0xFF },
{ "no-route", 1, 0, 0 },
{ "communication-prohibited", 1, 1, 1 },
+ { "beyond-scope", 1, 2, 2 },
{ "address-unreachable", 1, 3, 3 },
{ "port-unreachable", 1, 4, 4 },
+ { "failed-policy", 1, 5, 5 },
+ { "reject-route", 1, 6, 6 },
{ "packet-too-big", 2, 0, 0xFF },
@@ -219,6 +223,70 @@ static void icmp6_save(const void *ip, const struct xt_entry_match *match)
printf("/%u", icmpv6->code[0]);
}
+#define XT_ICMPV6_TYPE(type) (type - ND_ROUTER_SOLICIT)
+
+static const char *icmp6_type_xlate_array[] = {
+ [XT_ICMPV6_TYPE(ND_ROUTER_SOLICIT)] = "nd-router-solicit",
+ [XT_ICMPV6_TYPE(ND_ROUTER_ADVERT)] = "nd-router-advert",
+ [XT_ICMPV6_TYPE(ND_NEIGHBOR_SOLICIT)] = "nd-neighbor-solicit",
+ [XT_ICMPV6_TYPE(ND_NEIGHBOR_ADVERT)] = "nd-neighbor-advert",
+ [XT_ICMPV6_TYPE(ND_REDIRECT)] = "nd-redirect",
+};
+
+static const char *icmp6_type_xlate(unsigned int type)
+{
+ if (type < ND_ROUTER_SOLICIT || type > ND_REDIRECT)
+ return NULL;
+
+ return icmp6_type_xlate_array[XT_ICMPV6_TYPE(type)];
+}
+
+static unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype,
+ unsigned int code_min,
+ unsigned int code_max)
+{
+ unsigned int i;
+ const char *type_name;
+
+ if (code_min == code_max)
+ return 0;
+
+ type_name = icmp6_type_xlate(icmptype);
+
+ if (type_name) {
+ xt_xlate_add(xl, type_name);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i)
+ if (icmpv6_codes[i].type == icmptype &&
+ icmpv6_codes[i].code_min == code_min &&
+ icmpv6_codes[i].code_max == code_max)
+ break;
+
+ if (i != ARRAY_SIZE(icmpv6_codes))
+ xt_xlate_add(xl, icmpv6_codes[i].name);
+ else
+ return 0;
+ }
+
+ return 1;
+}
+
+static int icmp6_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ip6t_icmp *info = (struct ip6t_icmp *)params->match->data;
+
+ xt_xlate_add(xl, "icmpv6 type%s ",
+ (info->invflags & IP6T_ICMP_INV) ? " !=" : "");
+
+ if (!type_xlate_print(xl, info->type, info->code[0], info->code[1]))
+ return 0;
+
+ xt_xlate_add(xl, " ");
+
+ return 1;
+}
+
static struct xtables_match icmp6_mt6_reg = {
.name = "icmp6",
.version = XTABLES_VERSION,
@@ -231,6 +299,7 @@ static struct xtables_match icmp6_mt6_reg = {
.save = icmp6_save,
.x6_parse = icmp6_parse,
.x6_options = icmp6_opts,
+ .xlate = icmp6_xlate,
};
void _init(void)
diff --git a/extensions/libip6t_icmp6.t b/extensions/libip6t_icmp6.t
new file mode 100644
index 00000000..028cfc16
--- /dev/null
+++ b/extensions/libip6t_icmp6.t
@@ -0,0 +1,6 @@
+:INPUT,FORWARD,OUTPUT
+-m icmpv6;;FAIL
+-p ipv6-icmp -m icmp6 --icmpv6-type 1/0;=;OK
+-p ipv6-icmp -m icmp6 --icmpv6-type 2;=;OK
+# cannot use option twice:
+-p ipv6-icmp -m icmp6 --icmpv6-type no-route --icmpv6-type packet-too-big;;FAIL
diff --git a/extensions/libip6t_ipv6header.c b/extensions/libip6t_ipv6header.c
index 00d5d5b4..6f03087b 100644
--- a/extensions/libip6t_ipv6header.c
+++ b/extensions/libip6t_ipv6header.c
@@ -127,7 +127,7 @@ static void ipv6header_help(void)
printf(
"ipv6header match options:\n"
"[!] --header headers Type of header to match, by name\n"
-" names: hop,dst,route,frag,auth,esp,none,proto\n"
+" names: hop,dst,route,frag,auth,esp,none,prot\n"
" long names: hop-by-hop,ipv6-opts,ipv6-route,\n"
" ipv6-frag,ah,esp,ipv6-nonxt,protocol\n"
" numbers: 0,60,43,44,51,50,59\n"
diff --git a/extensions/libip6t_ipv6header.man b/extensions/libip6t_ipv6header.man
index a9988614..807d9abc 100644
--- a/extensions/libip6t_ipv6header.man
+++ b/extensions/libip6t_ipv6header.man
@@ -31,7 +31,7 @@ Encapsulating Security Payload header
No Next header which matches 59 in the 'Next Header field' of IPv6 header or
any IPv6 extension headers
.TP
-\fBproto\fP
+\fBprot\fP
which matches any upper layer protocol header. A protocol name from
/etc/protocols and numeric value also allowed. The number 255 is equivalent to
-\fBproto\fP.
+\fBprot\fP.
diff --git a/extensions/libip6t_ipv6header.t b/extensions/libip6t_ipv6header.t
new file mode 100644
index 00000000..67fa4799
--- /dev/null
+++ b/extensions/libip6t_ipv6header.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD,OUTPUT
+-m ipv6header --header hop-by-hop;=;OK
+-m ipv6header --header hop-by-hop --soft;=;OK
+-m ipv6header --header ipv6-nonxt;=;OK
diff --git a/extensions/libip6t_mh.c b/extensions/libip6t_mh.c
index 686a2932..f4c0fd9f 100644
--- a/extensions/libip6t_mh.c
+++ b/extensions/libip6t_mh.c
@@ -202,6 +202,26 @@ static void mh_save(const void *ip, const struct xt_entry_match *match)
printf(" --mh-type %u", mhinfo->types[0]);
}
+static int mh_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ip6t_mh *mhinfo = (struct ip6t_mh *)params->match->data;
+
+ if (mhinfo->types[0] == 0 && mhinfo->types[1] == 0xff)
+ return 1;
+
+ if (mhinfo->types[0] != mhinfo->types[1])
+ xt_xlate_add(xl, "mh type %s%u-%u",
+ mhinfo->invflags & IP6T_MH_INV_TYPE ? "!= " : "",
+ mhinfo->types[0], mhinfo->types[1]);
+ else
+ xt_xlate_add(xl, "mh type %s%u",
+ mhinfo->invflags & IP6T_MH_INV_TYPE ? "!= " : "",
+ mhinfo->types[0]);
+
+ return 1;
+}
+
static const struct xt_option_entry mh_opts[] = {
{.name = "mh-type", .id = O_MH_TYPE, .type = XTTYPE_STRING,
.flags = XTOPT_INVERT},
@@ -220,6 +240,7 @@ static struct xtables_match mh_mt6_reg = {
.print = mh_print,
.save = mh_save,
.x6_options = mh_opts,
+ .xlate = mh_xlate,
};
void _init(void)
diff --git a/extensions/libip6t_mh.t b/extensions/libip6t_mh.t
new file mode 100644
index 00000000..6b76d13d
--- /dev/null
+++ b/extensions/libip6t_mh.t
@@ -0,0 +1,6 @@
+:INPUT,FORWARD,OUTPUT
+-m mh;;FAIL
+-p mobility-header -m mh;=;OK
+-p mobility-header -m mh --mh-type 1;=;OK
+-p mobility-header -m mh ! --mh-type 4;=;OK
+-p mobility-header -m mh --mh-type 4:123;=;OK
diff --git a/extensions/libip6t_rt.c b/extensions/libip6t_rt.c
index d470488d..3cb3b249 100644
--- a/extensions/libip6t_rt.c
+++ b/extensions/libip6t_rt.c
@@ -99,6 +99,13 @@ parse_addresses(const char *addrstr, struct in6_addr *addrp)
return i;
}
+static void rt_init(struct xt_entry_match *m)
+{
+ struct ip6t_rt *rtinfo = (void *)m->data;
+
+ rtinfo->segsleft[1] = ~0U;
+}
+
static void rt_parse(struct xt_option_call *cb)
{
struct ip6t_rt *rtinfo = cb->data;
@@ -238,6 +245,43 @@ static void rt_save(const void *ip, const struct xt_entry_match *match)
}
+static int rt_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ip6t_rt *rtinfo = (struct ip6t_rt *)params->match->data;
+ char *space = "";
+
+ if (rtinfo->flags & IP6T_RT_TYP) {
+ xt_xlate_add(xl, "rt type%s %u",
+ (rtinfo->invflags & IP6T_RT_INV_TYP) ? " !=" : "",
+ rtinfo->rt_type);
+ space = " ";
+ }
+
+ if (!(rtinfo->segsleft[0] == 0 && rtinfo->segsleft[1] == 0xFFFFFFFF)) {
+ xt_xlate_add(xl, "%srt seg-left%s ", space,
+ (rtinfo->invflags & IP6T_RT_INV_SGS) ? " !=" : "");
+
+ if (rtinfo->segsleft[0] != rtinfo->segsleft[1])
+ xt_xlate_add(xl, "%u-%u", rtinfo->segsleft[0],
+ rtinfo->segsleft[1]);
+ else
+ xt_xlate_add(xl, "%u", rtinfo->segsleft[0]);
+ space = " ";
+ }
+
+ if (rtinfo->flags & IP6T_RT_LEN) {
+ xt_xlate_add(xl, "%srt hdrlength%s %u", space,
+ (rtinfo->invflags & IP6T_RT_INV_LEN) ? " !=" : "",
+ rtinfo->hdrlen);
+ }
+
+ if (rtinfo->flags & (IP6T_RT_RES | IP6T_RT_FST | IP6T_RT_FST_NSTRICT))
+ return 0;
+
+ return 1;
+}
+
static struct xtables_match rt_mt6_reg = {
.name = "rt",
.version = XTABLES_VERSION,
@@ -245,10 +289,12 @@ static struct xtables_match rt_mt6_reg = {
.size = XT_ALIGN(sizeof(struct ip6t_rt)),
.userspacesize = XT_ALIGN(sizeof(struct ip6t_rt)),
.help = rt_help,
+ .init = rt_init,
.x6_parse = rt_parse,
.print = rt_print,
.save = rt_save,
.x6_options = rt_opts,
+ .xlate = rt_xlate,
};
void
diff --git a/extensions/libip6t_rt.t b/extensions/libip6t_rt.t
new file mode 100644
index 00000000..3c7b2d98
--- /dev/null
+++ b/extensions/libip6t_rt.t
@@ -0,0 +1,5 @@
+:INPUT,FORWARD,OUTPUT
+-m rt --rt-type 0 --rt-segsleft 1:23 --rt-len 42 --rt-0-res;=;OK
+-m rt --rt-type 0 ! --rt-segsleft 1:23 ! --rt-len 42 --rt-0-res;=;OK
+-m rt ! --rt-type 1 ! --rt-segsleft 12:23 ! --rt-len 42;=;OK
+-m rt;=;OK
diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
index ff187999..a14d16f7 100644
--- a/extensions/libipt_DNAT.c
+++ b/extensions/libipt_DNAT.c
@@ -242,6 +242,51 @@ static void DNAT_save(const void *ip, const struct xt_entry_target *target)
}
}
+static void print_range_xlate(const struct nf_nat_ipv4_range *r,
+ struct xt_xlate *xl)
+{
+ if (r->flags & NF_NAT_RANGE_MAP_IPS) {
+ struct in_addr a;
+
+ a.s_addr = r->min_ip;
+ xt_xlate_add(xl, "%s", xtables_ipaddr_to_numeric(&a));
+ if (r->max_ip != r->min_ip) {
+ a.s_addr = r->max_ip;
+ xt_xlate_add(xl, "-%s", xtables_ipaddr_to_numeric(&a));
+ }
+ }
+ if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+ xt_xlate_add(xl, ":%hu", ntohs(r->min.tcp.port));
+ if (r->max.tcp.port != r->min.tcp.port)
+ xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port));
+ }
+}
+
+static int DNAT_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct ipt_natinfo *info = (const void *)params->target;
+ unsigned int i = 0;
+ bool sep_need = false;
+ const char *sep = " ";
+
+ for (i = 0; i < info->mr.rangesize; i++) {
+ xt_xlate_add(xl, "dnat to ");
+ print_range_xlate(&info->mr.range[i], xl);
+ if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM) {
+ xt_xlate_add(xl, " random");
+ sep_need = true;
+ }
+ if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT) {
+ if (sep_need)
+ sep = ",";
+ xt_xlate_add(xl, "%spersistent", sep);
+ }
+ }
+
+ return 1;
+}
+
static struct xtables_target dnat_tg_reg = {
.name = "DNAT",
.version = XTABLES_VERSION,
@@ -254,6 +299,7 @@ static struct xtables_target dnat_tg_reg = {
.print = DNAT_print,
.save = DNAT_save,
.x6_options = DNAT_opts,
+ .xlate = DNAT_xlate,
};
void _init(void)
diff --git a/extensions/libipt_DNAT.t b/extensions/libipt_DNAT.t
new file mode 100644
index 00000000..e3fd5632
--- /dev/null
+++ b/extensions/libipt_DNAT.t
@@ -0,0 +1,8 @@
+:PREROUTING
+*nat
+-j DNAT --to-destination 1.1.1.1;=;OK
+-j DNAT --to-destination 1.1.1.1-1.1.1.10;=;OK
+-p tcp -j DNAT --to-destination 1.1.1.1:1025-65535;=;OK
+-p tcp -j DNAT --to-destination 1.1.1.1-1.1.1.10:1025-65535;=;OK
+-p tcp -j DNAT --to-destination 1.1.1.1-1.1.1.10:1025-65536;;FAIL
+-j DNAT;;FAIL
diff --git a/extensions/libipt_ECN.t b/extensions/libipt_ECN.t
new file mode 100644
index 00000000..2e092052
--- /dev/null
+++ b/extensions/libipt_ECN.t
@@ -0,0 +1,5 @@
+:PREROUTING,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j ECN;;FAIL
+-p tcp -j ECN;;FAIL
+-p tcp -j ECN --ecn-tcp-remove;=;OK
diff --git a/extensions/libipt_LOG.c b/extensions/libipt_LOG.c
index 77f16d19..36e2e73b 100644
--- a/extensions/libipt_LOG.c
+++ b/extensions/libipt_LOG.c
@@ -63,6 +63,11 @@ struct ipt_log_names {
unsigned int level;
};
+struct ipt_log_xlate {
+ const char *name;
+ unsigned int level;
+};
+
static const struct ipt_log_names ipt_log_names[]
= { { .name = "alert", .level = LOG_ALERT },
{ .name = "crit", .level = LOG_CRIT },
@@ -166,6 +171,64 @@ static void LOG_save(const void *ip, const struct xt_entry_target *target)
printf(" --log-macdecode");
}
+static const struct ipt_log_xlate ipt_log_xlate_names[] = {
+ {"alert", LOG_ALERT },
+ {"crit", LOG_CRIT },
+ {"debug", LOG_DEBUG },
+ {"emerg", LOG_EMERG },
+ {"err", LOG_ERR },
+ {"info", LOG_INFO },
+ {"notice", LOG_NOTICE },
+ {"warn", LOG_WARNING }
+};
+
+static int LOG_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct ipt_log_info *loginfo =
+ (const struct ipt_log_info *)params->target->data;
+ unsigned int i = 0;
+
+ xt_xlate_add(xl, "log");
+ if (strcmp(loginfo->prefix, "") != 0) {
+ if (params->escape_quotes)
+ xt_xlate_add(xl, " prefix \\\"%s\\\"", loginfo->prefix);
+ else
+ xt_xlate_add(xl, " prefix \"%s\"", loginfo->prefix);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ipt_log_xlate_names); ++i)
+ if (loginfo->level != LOG_DEFAULT_LEVEL &&
+ loginfo->level == ipt_log_xlate_names[i].level) {
+ xt_xlate_add(xl, " level %s",
+ ipt_log_xlate_names[i].name);
+ break;
+ }
+
+ if ((loginfo->logflags & IPT_LOG_MASK) == IPT_LOG_MASK) {
+ xt_xlate_add(xl, " flags all");
+ } else {
+ if (loginfo->logflags & (IPT_LOG_TCPSEQ | IPT_LOG_TCPOPT)) {
+ const char *delim = " ";
+
+ xt_xlate_add(xl, " flags tcp");
+ if (loginfo->logflags & IPT_LOG_TCPSEQ) {
+ xt_xlate_add(xl, " sequence");
+ delim = ",";
+ }
+ if (loginfo->logflags & IPT_LOG_TCPOPT)
+ xt_xlate_add(xl, "%soptions", delim);
+ }
+ if (loginfo->logflags & IPT_LOG_IPOPT)
+ xt_xlate_add(xl, " flags ip options");
+ if (loginfo->logflags & IPT_LOG_UID)
+ xt_xlate_add(xl, " flags skuid");
+ if (loginfo->logflags & IPT_LOG_MACDECODE)
+ xt_xlate_add(xl, " flags ether");
+ }
+
+ return 1;
+}
static struct xtables_target log_tg_reg = {
.name = "LOG",
.version = XTABLES_VERSION,
@@ -178,6 +241,7 @@ static struct xtables_target log_tg_reg = {
.save = LOG_save,
.x6_parse = LOG_parse,
.x6_options = LOG_opts,
+ .xlate = LOG_xlate,
};
void _init(void)
diff --git a/extensions/libipt_LOG.t b/extensions/libipt_LOG.t
new file mode 100644
index 00000000..fbf5118b
--- /dev/null
+++ b/extensions/libipt_LOG.t
@@ -0,0 +1,12 @@
+:INPUT,FORWARD,OUTPUT
+-j LOG;-j LOG;OK
+-j LOG --log-prefix "test: ";=;OK
+-j LOG --log-prefix "test: " --log-level 1;=;OK
+# iptables displays the log-level output using the number; not the string
+-j LOG --log-prefix "test: " --log-level alert;-j LOG --log-prefix "test: " --log-level 1;OK
+-j LOG --log-prefix "test: " --log-tcp-sequence;=;OK
+-j LOG --log-prefix "test: " --log-tcp-options;=;OK
+-j LOG --log-prefix "test: " --log-ip-options;=;OK
+-j LOG --log-prefix "test: " --log-uid;=;OK
+-j LOG --log-prefix "test: " --log-level bad;;FAIL
+-j LOG --log-prefix;;FAIL
diff --git a/extensions/libipt_MASQUERADE.c b/extensions/libipt_MASQUERADE.c
index ea074454..b7b5fc74 100644
--- a/extensions/libipt_MASQUERADE.c
+++ b/extensions/libipt_MASQUERADE.c
@@ -134,6 +134,28 @@ MASQUERADE_save(const void *ip, const struct xt_entry_target *target)
printf(" --random");
}
+static int MASQUERADE_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct nf_nat_ipv4_multi_range_compat *mr =
+ (const void *)params->target->data;
+ const struct nf_nat_ipv4_range *r = &mr->range[0];
+
+ xt_xlate_add(xl, "masquerade");
+
+ if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+ xt_xlate_add(xl, " to :%hu", ntohs(r->min.tcp.port));
+ if (r->max.tcp.port != r->min.tcp.port)
+ xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port));
+ }
+
+ xt_xlate_add(xl, " ");
+ if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
+ xt_xlate_add(xl, "random ");
+
+ return 1;
+}
+
static struct xtables_target masquerade_tg_reg = {
.name = "MASQUERADE",
.version = XTABLES_VERSION,
@@ -146,6 +168,7 @@ static struct xtables_target masquerade_tg_reg = {
.print = MASQUERADE_print,
.save = MASQUERADE_save,
.x6_options = MASQUERADE_opts,
+ .xlate = MASQUERADE_xlate,
};
void _init(void)
diff --git a/extensions/libipt_MASQUERADE.t b/extensions/libipt_MASQUERADE.t
new file mode 100644
index 00000000..46502040
--- /dev/null
+++ b/extensions/libipt_MASQUERADE.t
@@ -0,0 +1,8 @@
+:POSTROUTING
+*nat
+-j MASQUERADE;=;OK
+-j MASQUERADE --random;=;OK
+-p tcp -j MASQUERADE --to-ports 1024;=;OK
+-p udp -j MASQUERADE --to-ports 1024-65535;=;OK
+-p udp -j MASQUERADE --to-ports 1024-65536;;FAIL
+-p udp -j MASQUERADE --to-ports -1;;FAIL
diff --git a/extensions/libipt_MIRROR.c b/extensions/libipt_MIRROR.c
deleted file mode 100644
index fb78751d..00000000
--- a/extensions/libipt_MIRROR.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Shared library add-on to iptables to add MIRROR target support. */
-#include <xtables.h>
-
-static struct xtables_target mirror_tg_reg = {
- .name = "MIRROR",
- .version = XTABLES_VERSION,
- .family = NFPROTO_IPV4,
- .size = XT_ALIGN(0),
- .userspacesize = XT_ALIGN(0),
-};
-
-void _init(void)
-{
- xtables_register_target(&mirror_tg_reg);
-}
diff --git a/extensions/libipt_MIRROR.man b/extensions/libipt_MIRROR.man
deleted file mode 100644
index 7b720bcb..00000000
--- a/extensions/libipt_MIRROR.man
+++ /dev/null
@@ -1,12 +0,0 @@
-This is an experimental demonstration target which inverts the source
-and destination fields in the IP header and retransmits the packet.
-It is only valid in the
-.BR INPUT ,
-.B FORWARD
-and
-.B PREROUTING
-chains, and user-defined chains which are only called from those
-chains. Note that the outgoing packets are
-.B NOT
-seen by any packet filtering chains, connection tracking or NAT, to
-avoid loops and other problems.
diff --git a/extensions/libipt_NETMAP.c b/extensions/libipt_NETMAP.c
index dee7b01b..f30615a3 100644
--- a/extensions/libipt_NETMAP.c
+++ b/extensions/libipt_NETMAP.c
@@ -62,8 +62,8 @@ static void NETMAP_parse(struct xt_option_call *cb)
range->max_ip = range->min_ip | ~cb->val.hmask.ip;
}
-static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
- int numeric)
+static void __NETMAP_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
{
const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
const struct nf_nat_ipv4_range *r = &mr->range[0];
@@ -80,10 +80,17 @@ static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
printf("/%d", bits);
}
+static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ printf(" to:");
+ __NETMAP_print(ip, target, numeric);
+}
+
static void NETMAP_save(const void *ip, const struct xt_entry_target *target)
{
printf(" --%s ", NETMAP_opts[0].name);
- NETMAP_print(ip, target, 0);
+ __NETMAP_print(ip, target, 0);
}
static struct xtables_target netmap_tg_reg = {
diff --git a/extensions/libipt_NETMAP.t b/extensions/libipt_NETMAP.t
new file mode 100644
index 00000000..31924b98
--- /dev/null
+++ b/extensions/libipt_NETMAP.t
@@ -0,0 +1,4 @@
+:PREROUTING,INPUT,OUTPUT,POSTROUTING
+*nat
+-j NETMAP --to 1.2.3.0/24;=;OK
+-j NETMAP --to 1.2.3.4;=;OK
diff --git a/extensions/libipt_REDIRECT.c b/extensions/libipt_REDIRECT.c
index 610a9499..7850306f 100644
--- a/extensions/libipt_REDIRECT.c
+++ b/extensions/libipt_REDIRECT.c
@@ -135,6 +135,24 @@ static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
}
}
+static int REDIRECT_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct nf_nat_ipv4_multi_range_compat *mr =
+ (const void *)params->target->data;
+ const struct nf_nat_ipv4_range *r = &mr->range[0];
+
+ if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+ xt_xlate_add(xl, "redirect to :%hu", ntohs(r->min.tcp.port));
+ if (r->max.tcp.port != r->min.tcp.port)
+ xt_xlate_add(xl, "-%hu ", ntohs(r->max.tcp.port));
+ if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
+ xt_xlate_add(xl, " random ");
+ }
+
+ return 1;
+}
+
static struct xtables_target redirect_tg_reg = {
.name = "REDIRECT",
.version = XTABLES_VERSION,
@@ -147,6 +165,7 @@ static struct xtables_target redirect_tg_reg = {
.print = REDIRECT_print,
.save = REDIRECT_save,
.x6_options = REDIRECT_opts,
+ .xlate = REDIRECT_xlate,
};
void _init(void)
diff --git a/extensions/libipt_REDIRECT.t b/extensions/libipt_REDIRECT.t
new file mode 100644
index 00000000..a0fb0ed1
--- /dev/null
+++ b/extensions/libipt_REDIRECT.t
@@ -0,0 +1,6 @@
+:PREROUTING,OUTPUT
+*nat
+-p tcp -j REDIRECT --to-ports 42;=;OK
+-p udp -j REDIRECT --to-ports 42-1234;=;OK
+-p tcp -j REDIRECT --to-ports 42-1234 --random;=;OK
+-j REDIRECT --to-ports 42;;FAIL
diff --git a/extensions/libipt_REJECT.c b/extensions/libipt_REJECT.c
index 362c65ed..ba815bae 100644
--- a/extensions/libipt_REJECT.c
+++ b/extensions/libipt_REJECT.c
@@ -24,6 +24,11 @@ struct reject_names {
const char *desc;
};
+struct reject_names_xlate {
+ const char *name;
+ enum ipt_reject_with with;
+};
+
enum {
O_REJECT_WITH = 0,
};
@@ -129,8 +134,8 @@ static void REJECT_print(const void *ip, const struct xt_entry_target *target,
static void REJECT_save(const void *ip, const struct xt_entry_target *target)
{
- const struct ipt_reject_info *reject
- = (const struct ipt_reject_info *)target->data;
+ const struct ipt_reject_info *reject =
+ (const struct ipt_reject_info *)target->data;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(reject_table); ++i)
@@ -140,6 +145,45 @@ static void REJECT_save(const void *ip, const struct xt_entry_target *target)
printf(" --reject-with %s", reject_table[i].name);
}
+static const struct reject_names_xlate reject_table_xlate[] = {
+ {"net-unreachable", IPT_ICMP_NET_UNREACHABLE},
+ {"host-unreachable", IPT_ICMP_HOST_UNREACHABLE},
+ {"prot-unreachable", IPT_ICMP_PROT_UNREACHABLE},
+ {"port-unreachable", IPT_ICMP_PORT_UNREACHABLE},
+#if 0
+ {"echo-reply", IPT_ICMP_ECHOREPLY},
+#endif
+ {"net-prohibited", IPT_ICMP_NET_PROHIBITED},
+ {"host-prohibited", IPT_ICMP_HOST_PROHIBITED},
+ {"tcp reset", IPT_TCP_RESET},
+ {"admin-prohibited", IPT_ICMP_ADMIN_PROHIBITED}
+};
+
+static int REJECT_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct ipt_reject_info *reject =
+ (const struct ipt_reject_info *)params->target->data;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(reject_table_xlate); ++i) {
+ if (reject_table_xlate[i].with == reject->with)
+ break;
+ }
+
+ if (reject->with == IPT_ICMP_PORT_UNREACHABLE)
+ xt_xlate_add(xl, "reject");
+ else if (reject->with == IPT_TCP_RESET)
+ xt_xlate_add(xl, "reject with %s",
+ reject_table_xlate[i].name);
+ else
+ xt_xlate_add(xl, "reject with icmp type %s",
+ reject_table_xlate[i].name);
+
+ return 1;
+}
+
+
static struct xtables_target reject_tg_reg = {
.name = "REJECT",
.version = XTABLES_VERSION,
@@ -152,6 +196,7 @@ static struct xtables_target reject_tg_reg = {
.save = REJECT_save,
.x6_parse = REJECT_parse,
.x6_options = REJECT_opts,
+ .xlate = REJECT_xlate,
};
void _init(void)
diff --git a/extensions/libipt_REJECT.man b/extensions/libipt_REJECT.man
index c419a85e..8a360ce7 100644
--- a/extensions/libipt_REJECT.man
+++ b/extensions/libipt_REJECT.man
@@ -18,9 +18,9 @@ The type given can be
\fBicmp\-port\-unreachable\fP,
\fBicmp\-proto\-unreachable\fP,
\fBicmp\-net\-prohibited\fP,
-\fBicmp\-host\-prohibited\fP or
-\fBicmp\-admin\-prohibited\fP (*)
-which return the appropriate ICMP error message (\fBport\-unreachable\fP is
+\fBicmp\-host\-prohibited\fP, or
+\fBicmp\-admin\-prohibited\fP (*),
+which return the appropriate ICMP error message (\fBicmp\-port\-unreachable\fP is
the default). The option
\fBtcp\-reset\fP
can be used on rules which only match the TCP protocol: this causes a
@@ -28,5 +28,5 @@ TCP RST packet to be sent back. This is mainly useful for blocking
.I ident
(113/tcp) probes which frequently occur when sending mail to broken mail
hosts (which won't accept your mail otherwise).
-.PP
+.IP
(*) Using icmp\-admin\-prohibited with kernels that do not support it will result in a plain DROP instead of REJECT
diff --git a/extensions/libipt_REJECT.t b/extensions/libipt_REJECT.t
new file mode 100644
index 00000000..5b26b107
--- /dev/null
+++ b/extensions/libipt_REJECT.t
@@ -0,0 +1,9 @@
+:INPUT,FORWARD,OUTPUT
+-j REJECT;=;OK
+-j REJECT --reject-with icmp-net-unreachable;=;OK
+-j REJECT --reject-with icmp-host-unreachable;=;OK
+-j REJECT --reject-with icmp-port-unreachable;=;OK
+-j REJECT --reject-with icmp-proto-unreachable;=;OK
+-j REJECT --reject-with icmp-net-prohibited;=;OK
+-j REJECT --reject-with icmp-host-prohibited;=;OK
+-j REJECT --reject-with icmp-admin-prohibited;=;OK
diff --git a/extensions/libipt_SAME.c b/extensions/libipt_SAME.c
deleted file mode 100644
index 5d5bf630..00000000
--- a/extensions/libipt_SAME.c
+++ /dev/null
@@ -1,186 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <xtables.h>
-#include <linux/netfilter/nf_nat.h>
-#include <linux/netfilter_ipv4/ipt_SAME.h>
-
-enum {
- O_TO_ADDR = 0,
- O_NODST,
- O_RANDOM,
- F_TO_ADDR = 1 << O_TO_ADDR,
- F_RANDOM = 1 << O_RANDOM,
-};
-
-static void SAME_help(void)
-{
- printf(
-"SAME target options:\n"
-" --to <ipaddr>-<ipaddr>\n"
-" Addresses to map source to.\n"
-" May be specified more than\n"
-" once for multiple ranges.\n"
-" --nodst\n"
-" Don't use destination-ip in\n"
-" source selection\n"
-" --random\n"
-" Randomize source port\n");
-}
-
-static const struct xt_option_entry SAME_opts[] = {
- {.name = "to", .id = O_TO_ADDR, .type = XTTYPE_STRING,
- .flags = XTOPT_MAND},
- {.name = "nodst", .id = O_NODST, .type = XTTYPE_NONE},
- {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
- XTOPT_TABLEEND,
-};
-
-/* Parses range of IPs */
-static void parse_to(const char *orig_arg, struct nf_nat_ipv4_range *range)
-{
- char *dash, *arg;
- const struct in_addr *ip;
-
- arg = strdup(orig_arg);
- if (arg == NULL)
- xtables_error(RESOURCE_PROBLEM, "strdup");
- range->flags |= NF_NAT_RANGE_MAP_IPS;
- dash = strchr(arg, '-');
-
- if (dash)
- *dash = '\0';
-
- ip = xtables_numeric_to_ipaddr(arg);
- if (!ip)
- xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
- arg);
- range->min_ip = ip->s_addr;
-
- if (dash) {
- ip = xtables_numeric_to_ipaddr(dash+1);
- if (!ip)
- xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
- dash+1);
- }
- range->max_ip = ip->s_addr;
- if (dash)
- if (range->min_ip > range->max_ip)
- xtables_error(PARAMETER_PROBLEM, "Bad IP range \"%s-%s\"\n",
- arg, dash+1);
- free(arg);
-}
-
-static void SAME_parse(struct xt_option_call *cb)
-{
- struct ipt_same_info *mr = cb->data;
- unsigned int count;
-
- xtables_option_parse(cb);
- switch (cb->entry->id) {
- case O_TO_ADDR:
- if (mr->rangesize == IPT_SAME_MAX_RANGE)
- xtables_error(PARAMETER_PROBLEM,
- "Too many ranges specified, maximum "
- "is %i ranges.\n",
- IPT_SAME_MAX_RANGE);
- parse_to(cb->arg, &mr->range[mr->rangesize]);
- mr->rangesize++;
- break;
- case O_NODST:
- mr->info |= IPT_SAME_NODST;
- break;
- case O_RANDOM:
- for (count=0; count < mr->rangesize; count++)
- mr->range[count].flags |= NF_NAT_RANGE_PROTO_RANDOM;
- break;
- }
-}
-
-static void SAME_fcheck(struct xt_fcheck_call *cb)
-{
- static const unsigned int f = F_TO_ADDR | F_RANDOM;
- struct ipt_same_info *mr = cb->data;
- unsigned int count;
-
- if ((cb->xflags & f) == f)
- for (count = 0; count < mr->rangesize; ++count)
- mr->range[count].flags |= NF_NAT_RANGE_PROTO_RANDOM;
-}
-
-static void SAME_print(const void *ip, const struct xt_entry_target *target,
- int numeric)
-{
- unsigned int count;
- const struct ipt_same_info *mr = (const void *)target->data;
- int random_selection = 0;
-
- printf(" same:");
-
- for (count = 0; count < mr->rangesize; count++) {
- const struct nf_nat_ipv4_range *r = &mr->range[count];
- struct in_addr a;
-
- a.s_addr = r->min_ip;
-
- printf("%s", xtables_ipaddr_to_numeric(&a));
- a.s_addr = r->max_ip;
-
- if (r->min_ip != r->max_ip)
- printf("-%s", xtables_ipaddr_to_numeric(&a));
- if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
- random_selection = 1;
- }
-
- if (mr->info & IPT_SAME_NODST)
- printf(" nodst");
-
- if (random_selection)
- printf(" random");
-}
-
-static void SAME_save(const void *ip, const struct xt_entry_target *target)
-{
- unsigned int count;
- const struct ipt_same_info *mr = (const void *)target->data;
- int random_selection = 0;
-
- for (count = 0; count < mr->rangesize; count++) {
- const struct nf_nat_ipv4_range *r = &mr->range[count];
- struct in_addr a;
-
- a.s_addr = r->min_ip;
- printf(" --to %s", xtables_ipaddr_to_numeric(&a));
- a.s_addr = r->max_ip;
-
- if (r->min_ip != r->max_ip)
- printf("-%s", xtables_ipaddr_to_numeric(&a));
- if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
- random_selection = 1;
- }
-
- if (mr->info & IPT_SAME_NODST)
- printf(" --nodst");
-
- if (random_selection)
- printf(" --random");
-}
-
-static struct xtables_target same_tg_reg = {
- .name = "SAME",
- .version = XTABLES_VERSION,
- .family = NFPROTO_IPV4,
- .size = XT_ALIGN(sizeof(struct ipt_same_info)),
- .userspacesize = XT_ALIGN(sizeof(struct ipt_same_info)),
- .help = SAME_help,
- .x6_parse = SAME_parse,
- .x6_fcheck = SAME_fcheck,
- .print = SAME_print,
- .save = SAME_save,
- .x6_options = SAME_opts,
-};
-
-void _init(void)
-{
- xtables_register_target(&same_tg_reg);
-}
diff --git a/extensions/libipt_SAME.man b/extensions/libipt_SAME.man
deleted file mode 100644
index a99dc73f..00000000
--- a/extensions/libipt_SAME.man
+++ /dev/null
@@ -1,17 +0,0 @@
-Similar to SNAT/DNAT depending on chain: it takes a range of addresses
-(`\-\-to 1.2.3.4\-1.2.3.7') and gives a client the same
-source-/destination-address for each connection.
-.PP
-N.B.: The DNAT target's \fB\-\-persistent\fP option replaced the SAME target.
-.TP
-\fB\-\-to\fP \fIipaddr\fP[\fB\-\fP\fIipaddr\fP]
-Addresses to map source to. May be specified more than once for
-multiple ranges.
-.TP
-\fB\-\-nodst\fP
-Don't use the destination-ip in the calculations when selecting the
-new source-ip
-.TP
-\fB\-\-random\fP
-Port mapping will be forcibly randomized to avoid attacks based on
-port prediction (kernel >= 2.6.21).
diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c
index 1a24f3d8..e92d811c 100644
--- a/extensions/libipt_SNAT.c
+++ b/extensions/libipt_SNAT.c
@@ -11,11 +11,13 @@
enum {
O_TO_SRC = 0,
O_RANDOM,
+ O_RANDOM_FULLY,
O_PERSISTENT,
O_X_TO_SRC,
- F_TO_SRC = 1 << O_TO_SRC,
- F_RANDOM = 1 << O_RANDOM,
- F_X_TO_SRC = 1 << O_X_TO_SRC,
+ F_TO_SRC = 1 << O_TO_SRC,
+ F_RANDOM = 1 << O_RANDOM,
+ F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
+ F_X_TO_SRC = 1 << O_X_TO_SRC,
};
/* Source NAT data consists of a multi-range, indicating where to map
@@ -32,13 +34,14 @@ static void SNAT_help(void)
"SNAT target options:\n"
" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
" Address to map source to.\n"
-"[--random] [--persistent]\n");
+"[--random] [--random-fully] [--persistent]\n");
}
static const struct xt_option_entry SNAT_opts[] = {
{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_MULTI},
{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+ {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
@@ -185,10 +188,13 @@ static void SNAT_parse(struct xt_option_call *cb)
static void SNAT_fcheck(struct xt_fcheck_call *cb)
{
static const unsigned int f = F_TO_SRC | F_RANDOM;
+ static const unsigned int r = F_TO_SRC | F_RANDOM_FULLY;
struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
if ((cb->xflags & f) == f)
mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
+ if ((cb->xflags & r) == r)
+ mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
}
static void print_range(const struct nf_nat_ipv4_range *r)
@@ -222,6 +228,8 @@ static void SNAT_print(const void *ip, const struct xt_entry_target *target,
print_range(&info->mr.range[i]);
if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
printf(" random");
+ if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
+ printf(" random-fully");
if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
printf(" persistent");
}
@@ -237,11 +245,65 @@ static void SNAT_save(const void *ip, const struct xt_entry_target *target)
print_range(&info->mr.range[i]);
if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
printf(" --random");
+ if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
+ printf(" --random-fully");
if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
printf(" --persistent");
}
}
+static void print_range_xlate(const struct nf_nat_ipv4_range *r,
+ struct xt_xlate *xl)
+{
+ if (r->flags & NF_NAT_RANGE_MAP_IPS) {
+ struct in_addr a;
+
+ a.s_addr = r->min_ip;
+ xt_xlate_add(xl, "%s", xtables_ipaddr_to_numeric(&a));
+ if (r->max_ip != r->min_ip) {
+ a.s_addr = r->max_ip;
+ xt_xlate_add(xl, "-%s", xtables_ipaddr_to_numeric(&a));
+ }
+ }
+ if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+ xt_xlate_add(xl, ":");
+ xt_xlate_add(xl, "%hu", ntohs(r->min.tcp.port));
+ if (r->max.tcp.port != r->min.tcp.port)
+ xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port));
+ }
+}
+
+static int SNAT_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct ipt_natinfo *info = (const void *)params->target;
+ unsigned int i = 0;
+ bool sep_need = false;
+ const char *sep = " ";
+
+ for (i = 0; i < info->mr.rangesize; i++) {
+ xt_xlate_add(xl, "snat to ");
+ print_range_xlate(&info->mr.range[i], xl);
+ if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM) {
+ xt_xlate_add(xl, " random");
+ sep_need = true;
+ }
+ if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
+ if (sep_need)
+ sep = ",";
+ xt_xlate_add(xl, "%sfully-random", sep);
+ sep_need = true;
+ }
+ if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT) {
+ if (sep_need)
+ sep = ",";
+ xt_xlate_add(xl, "%spersistent", sep);
+ }
+ }
+
+ return 1;
+}
+
static struct xtables_target snat_tg_reg = {
.name = "SNAT",
.version = XTABLES_VERSION,
@@ -254,6 +316,7 @@ static struct xtables_target snat_tg_reg = {
.print = SNAT_print,
.save = SNAT_save,
.x6_options = SNAT_opts,
+ .xlate = SNAT_xlate,
};
void _init(void)
diff --git a/extensions/libipt_SNAT.t b/extensions/libipt_SNAT.t
new file mode 100644
index 00000000..73071bb0
--- /dev/null
+++ b/extensions/libipt_SNAT.t
@@ -0,0 +1,8 @@
+:POSTROUTING
+*nat
+-j SNAT --to-source 1.1.1.1;=;OK
+-j SNAT --to-source 1.1.1.1-1.1.1.10;=;OK
+-p tcp -j SNAT --to-source 1.1.1.1:1025-65535;=;OK
+-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65535;=;OK
+-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65536;;FAIL
+-j SNAT;;FAIL
diff --git a/extensions/libipt_TTL.t b/extensions/libipt_TTL.t
new file mode 100644
index 00000000..36809792
--- /dev/null
+++ b/extensions/libipt_TTL.t
@@ -0,0 +1,10 @@
+:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j TTL --ttl-set 42;=;OK
+-j TTL --ttl-inc 1;=;OK
+-j TTL --ttl-dec 1;=;OK
+-j TTL --ttl-set 256;;FAIL
+-j TTL --ttl-inc 0;;FAIL
+-j TTL --ttl-dec 0;;FAIL
+-j TTL --ttl-dec 1 --ttl-inc 1;;FAIL
+-j TTL --ttl-set --ttl-inc 1;;FAIL
diff --git a/extensions/libipt_ULOG.t b/extensions/libipt_ULOG.t
new file mode 100644
index 00000000..97500b00
--- /dev/null
+++ b/extensions/libipt_ULOG.t
@@ -0,0 +1,19 @@
+:INPUT,FORWARD,OUTPUT
+-j ULOG --ulog-nlgroup 1;-j ULOG;OK
+-j ULOG --ulog-nlgroup 32;=;OK
+-j ULOG --ulog-nlgroup 33;;FAIL
+-j ULOG --ulog-nlgroup 0;;FAIL
+-j ULOG --ulog-cprange 1;=;OK
+-j ULOG --ulog-cprange 4294967295;=;OK
+# This below outputs 0 in iptables-save
+# ERROR: should fail: iptables -A INPUT -j ULOG --ulog-cprange 4294967296
+#-j ULOG --ulog-cprange 4294967296;;FAIL
+# supports up to 31 characters
+-j ULOG --ulog-prefix xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;=;OK
+# ERROR: should fail: iptables -A INPUT -j ULOG --ulog-prefix xxxxxx [...]
+#-j ULOG --ulog-prefix xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;;FAIL
+-j ULOG --ulog-qthreshold 1;-j ULOG;OK
+-j ULOG --ulog-qthreshold 0;;FAIL
+-j ULOG --ulog-qthreshold 50;=;OK
+-j ULOG --ulog-qthreshold 51;;FAIL
+-j ULOG;=;OK
diff --git a/extensions/libipt_ah.c b/extensions/libipt_ah.c
index 8cf167c4..fec5705c 100644
--- a/extensions/libipt_ah.c
+++ b/extensions/libipt_ah.c
@@ -21,6 +21,13 @@ static const struct xt_option_entry ah_opts[] = {
XTOPT_TABLEEND,
};
+static void ah_init(struct xt_entry_match *m)
+{
+ struct ipt_ah *ahinfo = (void *)m->data;
+
+ ahinfo->spis[1] = ~0U;
+}
+
static void ah_parse(struct xt_option_call *cb)
{
struct ipt_ah *ahinfo = cb->data;
@@ -85,17 +92,37 @@ static void ah_save(const void *ip, const struct xt_entry_match *match)
}
+static int ah_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ipt_ah *ahinfo = (struct ipt_ah *)params->match->data;
+
+ if (!(ahinfo->spis[0] == 0 && ahinfo->spis[1] == 0xFFFFFFFF)) {
+ xt_xlate_add(xl, "ah spi%s ",
+ (ahinfo->invflags & IPT_AH_INV_SPI) ? " !=" : "");
+ if (ahinfo->spis[0] != ahinfo->spis[1])
+ xt_xlate_add(xl, "%u-%u", ahinfo->spis[0],
+ ahinfo->spis[1]);
+ else
+ xt_xlate_add(xl, "%u", ahinfo->spis[0]);
+ }
+
+ return 1;
+}
+
static struct xtables_match ah_mt_reg = {
- .name = "ah",
- .version = XTABLES_VERSION,
+ .name = "ah",
+ .version = XTABLES_VERSION,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct ipt_ah)),
- .userspacesize = XT_ALIGN(sizeof(struct ipt_ah)),
- .help = ah_help,
- .print = ah_print,
- .save = ah_save,
+ .userspacesize = XT_ALIGN(sizeof(struct ipt_ah)),
+ .help = ah_help,
+ .init = ah_init,
+ .print = ah_print,
+ .save = ah_save,
.x6_parse = ah_parse,
.x6_options = ah_opts,
+ .xlate = ah_xlate,
};
void
diff --git a/extensions/libipt_ah.t b/extensions/libipt_ah.t
new file mode 100644
index 00000000..cd853865
--- /dev/null
+++ b/extensions/libipt_ah.t
@@ -0,0 +1,13 @@
+:INPUT,FORWARD,OUTPUT
+-p ah -m ah --ahspi 0;=;OK
+-p ah -m ah --ahspi 4294967295;=;OK
+-p ah -m ah --ahspi 0:4294967295;-p ah -m ah;OK
+-p ah -m ah ! --ahspi 0;=;OK
+-p ah -m ah --ahspi -1;;FAIL
+-p ah -m ah --ahspi 4294967296;;FAIL
+-p ah -m ah --ahspi invalid;;FAIL
+-p ah -m ah --ahspi 0:invalid;;FAIL
+-m ah --ahspi 0;;FAIL
+-m ah --ahspi;;FAIL
+-m ah;;FAIL
+-p ah -m ah;=;OK
diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c
index 666e7daf..680a5b0c 100644
--- a/extensions/libipt_icmp.c
+++ b/extensions/libipt_icmp.c
@@ -249,6 +249,41 @@ static void icmp_save(const void *ip, const struct xt_entry_match *match)
}
}
+static unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype,
+ unsigned int code_min,
+ unsigned int code_max)
+{
+ unsigned int i;
+
+ if (code_min != code_max) {
+ for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i)
+ if (icmp_codes[i].type == icmptype &&
+ icmp_codes[i].code_min == code_min &&
+ icmp_codes[i].code_max == code_max) {
+ xt_xlate_add(xl, icmp_codes[i].name);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int icmp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ipt_icmp *info = (struct ipt_icmp *)params->match->data;
+
+ if (info->type != 0xFF) {
+ xt_xlate_add(xl, "icmp type%s ",
+ (info->invflags & IPT_ICMP_INV) ? " !=" : "");
+
+ if (!type_xlate_print(xl, info->type, info->code[0],
+ info->code[1]))
+ return 0;
+ }
+ return 1;
+}
+
static struct xtables_match icmp_mt_reg = {
.name = "icmp",
.version = XTABLES_VERSION,
@@ -261,6 +296,7 @@ static struct xtables_match icmp_mt_reg = {
.save = icmp_save,
.x6_parse = icmp_parse,
.x6_options = icmp_opts,
+ .xlate = icmp_xlate,
};
void _init(void)
diff --git a/extensions/libipt_icmp.t b/extensions/libipt_icmp.t
new file mode 100644
index 00000000..f4ba65c2
--- /dev/null
+++ b/extensions/libipt_icmp.t
@@ -0,0 +1,15 @@
+:INPUT,FORWARD,OUTPUT
+-p icmp -m icmp --icmp-type any;=;OK
+# output uses the number, better use the name?
+# ERROR: cannot find: iptables -I INPUT -p icmp -m icmp --icmp-type echo-reply
+# -p icmp -m icmp --icmp-type echo-reply;=;OK
+# output uses the number, better use the name?
+# ERROR: annot find: iptables -I INPUT -p icmp -m icmp --icmp-type destination-unreachable
+# -p icmp -m icmp --icmp-type destination-unreachable;=;OK
+# it does not acccept name/name, should we accept this?
+# ERROR: cannot load: iptables -A INPUT -p icmp -m icmp --icmp-type destination-unreachable/network-unreachable
+# -p icmp -m icmp --icmp-type destination-unreachable/network-unreachable;=;OK
+-m icmp;;FAIL
+# we accept "iptables -I INPUT -p tcp -m tcp", why not this below?
+# ERROR: cannot load: iptables -A INPUT -p icmp -m icmp
+# -p icmp -m icmp;=;OK
diff --git a/extensions/libipt_realm.c b/extensions/libipt_realm.c
index a8d9dda0..8eea7874 100644
--- a/extensions/libipt_realm.c
+++ b/extensions/libipt_realm.c
@@ -34,6 +34,7 @@ static struct xtables_lmap *realms;
static void realm_init(struct xt_entry_match *m)
{
const char file[] = "/etc/iproute2/rt_realms";
+
realms = xtables_lmap_init(file);
if (realms == NULL && errno != ENOENT)
fprintf(stderr, "Warning: %s: %s\n", file, strerror(errno));
@@ -70,7 +71,7 @@ static void realm_parse(struct xt_option_call *cb)
static void
print_realm(unsigned long id, unsigned long mask, int numeric)
{
- const char* name = NULL;
+ const char *name = NULL;
if (mask != 0xffffffff)
printf(" 0x%lx/0x%lx", id, mask);
@@ -85,7 +86,7 @@ print_realm(unsigned long id, unsigned long mask, int numeric)
}
static void realm_print(const void *ip, const struct xt_entry_match *match,
- int numeric)
+ int numeric)
{
const struct xt_realm_info *ri = (const void *)match->data;
@@ -107,6 +108,42 @@ static void realm_save(const void *ip, const struct xt_entry_match *match)
print_realm(ri->id, ri->mask, 0);
}
+static void
+print_realm_xlate(unsigned long id, unsigned long mask,
+ int numeric, struct xt_xlate *xl, uint32_t op)
+{
+ const char *name = NULL;
+
+ if (mask != 0xffffffff)
+ xt_xlate_add(xl, " and 0x%lx %s 0x%lx", mask,
+ op == XT_OP_EQ ? "==" : "!=", id);
+ else {
+ if (numeric == 0)
+ name = xtables_lmap_id2name(realms, id);
+ if (name)
+ xt_xlate_add(xl, " %s%s",
+ op == XT_OP_EQ ? "" : "!= ", name);
+ else
+ xt_xlate_add(xl, " %s0x%lx",
+ op == XT_OP_EQ ? "" : "!= ", id);
+ }
+}
+
+static int realm_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_realm_info *ri = (const void *)params->match->data;
+ enum xt_op op = XT_OP_EQ;
+
+ if (ri->invert)
+ op = XT_OP_NEQ;
+
+ xt_xlate_add(xl, "rtclassid");
+ print_realm_xlate(ri->id, ri->mask, 0, xl, op);
+
+ return 1;
+}
+
static struct xtables_match realm_mt_reg = {
.name = "realm",
.version = XTABLES_VERSION,
@@ -119,6 +156,7 @@ static struct xtables_match realm_mt_reg = {
.save = realm_save,
.x6_parse = realm_parse,
.x6_options = realm_opts,
+ .xlate = realm_xlate,
};
void _init(void)
diff --git a/extensions/libipt_realm.t b/extensions/libipt_realm.t
new file mode 100644
index 00000000..ca666407
--- /dev/null
+++ b/extensions/libipt_realm.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD,OUTPUT
+-m realm --realm 0x1/0x2a;=;OK
+-m realm --realm 0x2a;=;OK
+-m realm;;FAIL
diff --git a/extensions/libipt_ttl.c b/extensions/libipt_ttl.c
index 5fe08ccd..6bdd2196 100644
--- a/extensions/libipt_ttl.c
+++ b/extensions/libipt_ttl.c
@@ -100,6 +100,35 @@ static void ttl_save(const void *ip, const struct xt_entry_match *match)
printf(" %u", info->ttl);
}
+static int ttl_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ipt_ttl_info *info =
+ (struct ipt_ttl_info *) params->match->data;
+
+ switch (info->mode) {
+ case IPT_TTL_EQ:
+ xt_xlate_add(xl, "ip ttl");
+ break;
+ case IPT_TTL_NE:
+ xt_xlate_add(xl, "ip ttl !=");
+ break;
+ case IPT_TTL_LT:
+ xt_xlate_add(xl, "ip ttl lt");
+ break;
+ case IPT_TTL_GT:
+ xt_xlate_add(xl, "ip ttl gt");
+ break;
+ default:
+ /* Should not happen. */
+ break;
+ }
+
+ xt_xlate_add(xl, " %u", info->ttl);
+
+ return 1;
+}
+
#define s struct ipt_ttl_info
static const struct xt_option_entry ttl_opts[] = {
{.name = "ttl-lt", .id = O_TTL_LT, .excl = F_ANY, .type = XTTYPE_UINT8,
@@ -126,6 +155,7 @@ static struct xtables_match ttl_mt_reg = {
.x6_parse = ttl_parse,
.x6_fcheck = ttl_check,
.x6_options = ttl_opts,
+ .xlate = ttl_xlate,
};
diff --git a/extensions/libipt_ttl.t b/extensions/libipt_ttl.t
new file mode 100644
index 00000000..ebe5b3a2
--- /dev/null
+++ b/extensions/libipt_ttl.t
@@ -0,0 +1,15 @@
+:INPUT,FORWARD,OUTPUT
+-m ttl --ttl-eq 0;=;OK
+-m ttl --ttl-eq 255;=;OK
+-m ttl ! --ttl-eq 0;=;OK
+-m ttl ! --ttl-eq 255;=;OK
+-m ttl --ttl-gt 0;=;OK
+# not possible have anything greater than 255, TTL is 8-bit long
+# ERROR: should fail: iptables -A INPUT -m ttl --ttl-gt 255
+## -m ttl --ttl-gt 255;;FAIL
+# not possible have anything below 0
+# ERROR: should fail: iptables -A INPUT -m ttl --ttl-lt 0
+## -m ttl --ttl-lt 0;;FAIL
+-m ttl --ttl-eq 256;;FAIL
+-m ttl --ttl-eq -1;;FAIL
+-m ttl;;FAIL
diff --git a/extensions/libipt_unclean.c b/extensions/libipt_unclean.c
deleted file mode 100644
index bc4a4a08..00000000
--- a/extensions/libipt_unclean.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Shared library add-on to iptables for unclean. */
-#include <xtables.h>
-
-static struct xtables_match unclean_mt_reg = {
- .name = "unclean",
- .version = XTABLES_VERSION,
- .family = NFPROTO_IPV4,
- .size = XT_ALIGN(0),
- .userspacesize = XT_ALIGN(0),
-};
-
-void _init(void)
-{
- xtables_register_match(&unclean_mt_reg);
-}
diff --git a/extensions/libipt_unclean.man b/extensions/libipt_unclean.man
deleted file mode 100644
index 3fecd554..00000000
--- a/extensions/libipt_unclean.man
+++ /dev/null
@@ -1,2 +0,0 @@
-This module takes no options, but attempts to match packets which seem
-malformed or unusual. This is regarded as experimental.
diff --git a/extensions/libxt_AUDIT.t b/extensions/libxt_AUDIT.t
new file mode 100644
index 00000000..97575b0e
--- /dev/null
+++ b/extensions/libxt_AUDIT.t
@@ -0,0 +1,6 @@
+:INPUT,FORWARD,OUTPUT
+-j AUDIT --type accept;=;OK
+-j AUDIT --type drop;=;OK
+-j AUDIT --type reject;=;OK
+-j AUDIT;;FAIL
+-j AUDIT --type wrong;;FAIL
diff --git a/extensions/libxt_CHECKSUM.t b/extensions/libxt_CHECKSUM.t
new file mode 100644
index 00000000..9451ad86
--- /dev/null
+++ b/extensions/libxt_CHECKSUM.t
@@ -0,0 +1,4 @@
+:PREROUTING,FORWARD,POSTROUTING
+*mangle
+-j CHECKSUM --checksum-fill;=;OK
+-j CHECKSUM;;FAIL
diff --git a/extensions/libxt_CLASSIFY.c b/extensions/libxt_CLASSIFY.c
index e04657ae..ba88f758 100644
--- a/extensions/libxt_CLASSIFY.c
+++ b/extensions/libxt_CLASSIFY.c
@@ -73,20 +73,67 @@ CLASSIFY_save(const void *ip, const struct xt_entry_target *target)
TC_H_MAJ(clinfo->priority)>>16, TC_H_MIN(clinfo->priority));
}
-static struct xtables_target classify_target = {
- .family = NFPROTO_UNSPEC,
- .name = "CLASSIFY",
- .version = XTABLES_VERSION,
- .size = XT_ALIGN(sizeof(struct xt_classify_target_info)),
- .userspacesize = XT_ALIGN(sizeof(struct xt_classify_target_info)),
- .help = CLASSIFY_help,
- .print = CLASSIFY_print,
- .save = CLASSIFY_save,
- .x6_parse = CLASSIFY_parse,
- .x6_options = CLASSIFY_opts,
+static void
+arpCLASSIFY_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ CLASSIFY_save(ip, target);
+}
+
+static int CLASSIFY_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_classify_target_info *clinfo =
+ (const struct xt_classify_target_info *)params->target->data;
+ __u32 handle = clinfo->priority;
+
+ xt_xlate_add(xl, "meta priority set ");
+
+ switch (handle) {
+ case TC_H_ROOT:
+ xt_xlate_add(xl, "root");
+ break;
+ case TC_H_UNSPEC:
+ xt_xlate_add(xl, "none");
+ break;
+ default:
+ xt_xlate_add(xl, "%0x:%0x", TC_H_MAJ(handle) >> 16,
+ TC_H_MIN(handle));
+ break;
+ }
+
+ return 1;
+}
+
+static struct xtables_target classify_target[] = {
+ {
+ .family = NFPROTO_UNSPEC,
+ .name = "CLASSIFY",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_classify_target_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_classify_target_info)),
+ .help = CLASSIFY_help,
+ .print = CLASSIFY_print,
+ .save = CLASSIFY_save,
+ .x6_parse = CLASSIFY_parse,
+ .x6_options = CLASSIFY_opts,
+ .xlate = CLASSIFY_xlate,
+ },
+ {
+ .family = NFPROTO_ARP,
+ .name = "CLASSIFY",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_classify_target_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_classify_target_info)),
+ .help = CLASSIFY_help,
+ .print = arpCLASSIFY_print,
+ .x6_parse = CLASSIFY_parse,
+ .x6_options = CLASSIFY_opts,
+ .xlate = CLASSIFY_xlate,
+ },
};
void _init(void)
{
- xtables_register_target(&classify_target);
+ xtables_register_targets(classify_target, ARRAY_SIZE(classify_target));
}
diff --git a/extensions/libxt_CLASSIFY.t b/extensions/libxt_CLASSIFY.t
new file mode 100644
index 00000000..7b3ddbf7
--- /dev/null
+++ b/extensions/libxt_CLASSIFY.t
@@ -0,0 +1,9 @@
+:FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j CLASSIFY --set-class 0000:ffff;=;OK
+# maximum handle accepted by tc is 0xffff
+# ERROR : should fail: iptables -A FORWARD -t mangle -j CLASSIFY --set-class 0000:ffffffff
+# -j CLASSIFY --set-class 0000:ffffffff;;FAIL
+# ERROR: should fail: iptables -A FORWARD -t mangle -j CLASSIFY --set-class 1:-1
+# -j CLASSIFY --set-class 1:-1;;FAIL
+-j CLASSIFY;;FAIL
diff --git a/extensions/libxt_CONNMARK.c b/extensions/libxt_CONNMARK.c
index 5d5351e3..f60be583 100644
--- a/extensions/libxt_CONNMARK.c
+++ b/extensions/libxt_CONNMARK.c
@@ -17,7 +17,7 @@
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdbool.h>
#include <stdint.h>
@@ -347,6 +347,50 @@ connmark_tg_save(const void *ip, const struct xt_entry_target *target)
}
}
+static int connmark_tg_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_connmark_tginfo1 *info =
+ (const void *)params->target->data;
+
+ switch (info->mode) {
+ case XT_CONNMARK_SET:
+ xt_xlate_add(xl, "ct mark set ");
+ if (info->ctmark == 0)
+ xt_xlate_add(xl, "ct mark and 0x%x", ~info->ctmask);
+ else if (info->ctmark == info->ctmask)
+ xt_xlate_add(xl, "ct mark or 0x%x",
+ info->ctmark);
+ else if (info->ctmask == 0)
+ xt_xlate_add(xl, "ct mark xor 0x%x",
+ info->ctmark);
+ else if (info->ctmask == 0xFFFFFFFFU)
+ xt_xlate_add(xl, "0x%x ", info->ctmark);
+ else
+ xt_xlate_add(xl, "ct mark xor 0x%x and 0x%x",
+ info->ctmark, ~info->ctmask);
+ break;
+ case XT_CONNMARK_SAVE:
+ xt_xlate_add(xl, "ct mark set mark");
+ if (!(info->nfmask == UINT32_MAX &&
+ info->ctmask == UINT32_MAX)) {
+ if (info->nfmask == info->ctmask)
+ xt_xlate_add(xl, " and 0x%x", info->nfmask);
+ }
+ break;
+ case XT_CONNMARK_RESTORE:
+ xt_xlate_add(xl, "meta mark set ct mark");
+ if (!(info->nfmask == UINT32_MAX &&
+ info->ctmask == UINT32_MAX)) {
+ if (info->nfmask == info->ctmask)
+ xt_xlate_add(xl, " and 0x%x", info->nfmask);
+ }
+ break;
+ }
+
+ return 1;
+}
+
static struct xtables_target connmark_tg_reg[] = {
{
.family = NFPROTO_UNSPEC,
@@ -377,6 +421,7 @@ static struct xtables_target connmark_tg_reg[] = {
.x6_parse = connmark_tg_parse,
.x6_fcheck = connmark_tg_check,
.x6_options = connmark_tg_opts,
+ .xlate = connmark_tg_xlate,
},
};
diff --git a/extensions/libxt_CONNMARK.t b/extensions/libxt_CONNMARK.t
new file mode 100644
index 00000000..79a838fe
--- /dev/null
+++ b/extensions/libxt_CONNMARK.t
@@ -0,0 +1,7 @@
+:PREROUTING,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j CONNMARK --restore-mark;=;OK
+-j CONNMARK --save-mark;=;OK
+-j CONNMARK --save-mark --nfmask 0xfffffff --ctmask 0xffffffff;-j CONNMARK --save-mark;OK
+-j CONNMARK --restore-mark --nfmask 0xfffffff --ctmask 0xffffffff;-j CONNMARK --restore-mark;OK
+-j CONNMARK;;FAIL
diff --git a/extensions/libxt_CONNSECMARK.t b/extensions/libxt_CONNSECMARK.t
new file mode 100644
index 00000000..2751b255
--- /dev/null
+++ b/extensions/libxt_CONNSECMARK.t
@@ -0,0 +1,5 @@
+:PREROUTING,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j CONNSECMARK --restore;=;OK
+-j CONNSECMARK --save;=;OK
+-j CONNSECMARK;;FAIL
diff --git a/extensions/libxt_CT.c b/extensions/libxt_CT.c
index 6b28fe1b..371b2176 100644
--- a/extensions/libxt_CT.c
+++ b/extensions/libxt_CT.c
@@ -16,7 +16,9 @@ static void ct_help(void)
" --helper name Use conntrack helper 'name' for connection\n"
" --ctevents event[,event...] Generate specified conntrack events for connection\n"
" --expevents event[,event...] Generate specified expectation events for connection\n"
-" --zone ID Assign/Lookup connection in zone ID\n"
+" --zone {ID|mark} Assign/Lookup connection in zone ID/packet nfmark\n"
+" --zone-orig {ID|mark} Same as 'zone' option, but only applies to ORIGINAL direction\n"
+" --zone-reply {ID|mark} Same as 'zone' option, but only applies to REPLY direction\n"
);
}
@@ -29,7 +31,9 @@ static void ct_help_v1(void)
" --timeout name Use timeout policy 'name' for connection\n"
" --ctevents event[,event...] Generate specified conntrack events for connection\n"
" --expevents event[,event...] Generate specified expectation events for connection\n"
-" --zone ID Assign/Lookup connection in zone ID\n"
+" --zone {ID|mark} Assign/Lookup connection in zone ID/packet nfmark\n"
+" --zone-orig {ID|mark} Same as 'zone' option, but only applies to ORIGINAL direction\n"
+" --zone-reply {ID|mark} Same as 'zone' option, but only applies to REPLY direction\n"
);
}
@@ -40,6 +44,8 @@ enum {
O_CTEVENTS,
O_EXPEVENTS,
O_ZONE,
+ O_ZONE_ORIG,
+ O_ZONE_REPLY,
};
#define s struct xt_ct_target_info
@@ -49,8 +55,9 @@ static const struct xt_option_entry ct_opts[] = {
.flags = XTOPT_PUT, XTOPT_POINTER(s, helper)},
{.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING},
{.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING},
- {.name = "zone", .id = O_ZONE, .type = XTTYPE_UINT16,
- .flags = XTOPT_PUT, XTOPT_POINTER(s, zone)},
+ {.name = "zone-orig", .id = O_ZONE_ORIG, .type = XTTYPE_STRING},
+ {.name = "zone-reply", .id = O_ZONE_REPLY, .type = XTTYPE_STRING},
+ {.name = "zone", .id = O_ZONE, .type = XTTYPE_STRING},
XTOPT_TABLEEND,
};
#undef s
@@ -64,8 +71,9 @@ static const struct xt_option_entry ct_opts_v1[] = {
.flags = XTOPT_PUT, XTOPT_POINTER(s, timeout)},
{.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING},
{.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING},
- {.name = "zone", .id = O_ZONE, .type = XTTYPE_UINT16,
- .flags = XTOPT_PUT, XTOPT_POINTER(s, zone)},
+ {.name = "zone-orig", .id = O_ZONE_ORIG, .type = XTTYPE_STRING},
+ {.name = "zone-reply", .id = O_ZONE_REPLY, .type = XTTYPE_STRING},
+ {.name = "zone", .id = O_ZONE, .type = XTTYPE_STRING},
XTOPT_TABLEEND,
};
#undef s
@@ -92,6 +100,45 @@ static const struct event_tbl exp_event_tbl[] = {
{ "new", IPEXP_NEW },
};
+static void ct_parse_zone_id(const char *opt, unsigned int opt_id,
+ uint16_t *zone_id, uint16_t *flags)
+{
+ if (opt_id == O_ZONE_ORIG)
+ *flags |= XT_CT_ZONE_DIR_ORIG;
+ if (opt_id == O_ZONE_REPLY)
+ *flags |= XT_CT_ZONE_DIR_REPL;
+
+ *zone_id = 0;
+
+ if (strcasecmp(opt, "mark") == 0) {
+ *flags |= XT_CT_ZONE_MARK;
+ } else {
+ uintmax_t val;
+
+ if (!xtables_strtoul(opt, NULL, &val, 0, UINT16_MAX))
+ xtables_error(PARAMETER_PROBLEM,
+ "Cannot parse %s as a zone ID\n", opt);
+
+ *zone_id = (uint16_t)val;
+ }
+}
+
+static void ct_print_zone_id(const char *pfx, uint16_t zone_id, uint16_t flags)
+{
+ printf(" %s", pfx);
+
+ if ((flags & (XT_CT_ZONE_DIR_ORIG |
+ XT_CT_ZONE_DIR_REPL)) == XT_CT_ZONE_DIR_ORIG)
+ printf("-orig");
+ if ((flags & (XT_CT_ZONE_DIR_ORIG |
+ XT_CT_ZONE_DIR_REPL)) == XT_CT_ZONE_DIR_REPL)
+ printf("-reply");
+ if (flags & XT_CT_ZONE_MARK)
+ printf(" mark");
+ else
+ printf(" %u", zone_id);
+}
+
static uint32_t ct_parse_events(const struct event_tbl *tbl, unsigned int size,
const char *events)
{
@@ -138,6 +185,12 @@ static void ct_parse(struct xt_option_call *cb)
case O_NOTRACK:
info->flags |= XT_CT_NOTRACK;
break;
+ case O_ZONE_ORIG:
+ case O_ZONE_REPLY:
+ case O_ZONE:
+ ct_parse_zone_id(cb->arg, cb->entry->id, &info->zone,
+ &info->flags);
+ break;
case O_CTEVENTS:
info->ct_events = ct_parse_events(ct_event_tbl, ARRAY_SIZE(ct_event_tbl), cb->arg);
break;
@@ -156,6 +209,12 @@ static void ct_parse_v1(struct xt_option_call *cb)
case O_NOTRACK:
info->flags |= XT_CT_NOTRACK;
break;
+ case O_ZONE_ORIG:
+ case O_ZONE_REPLY:
+ case O_ZONE:
+ ct_parse_zone_id(cb->arg, cb->entry->id, &info->zone,
+ &info->flags);
+ break;
case O_CTEVENTS:
info->ct_events = ct_parse_events(ct_event_tbl,
ARRAY_SIZE(ct_event_tbl),
@@ -185,8 +244,8 @@ static void ct_print(const void *ip, const struct xt_entry_target *target, int n
if (info->exp_events)
ct_print_events("expevents", exp_event_tbl,
ARRAY_SIZE(exp_event_tbl), info->exp_events);
- if (info->zone)
- printf("zone %u ", info->zone);
+ if (info->flags & XT_CT_ZONE_MARK || info->zone)
+ ct_print_zone_id("zone", info->zone, info->flags);
}
static void
@@ -212,8 +271,8 @@ ct_print_v1(const void *ip, const struct xt_entry_target *target, int numeric)
if (info->exp_events)
ct_print_events("expevents", exp_event_tbl,
ARRAY_SIZE(exp_event_tbl), info->exp_events);
- if (info->zone)
- printf("zone %u ", info->zone);
+ if (info->flags & XT_CT_ZONE_MARK || info->zone)
+ ct_print_zone_id("zone", info->zone, info->flags);
}
static void ct_save(const void *ip, const struct xt_entry_target *target)
@@ -233,8 +292,8 @@ static void ct_save(const void *ip, const struct xt_entry_target *target)
if (info->exp_events)
ct_print_events("--expevents", exp_event_tbl,
ARRAY_SIZE(exp_event_tbl), info->exp_events);
- if (info->zone)
- printf(" --zone %u", info->zone);
+ if (info->flags & XT_CT_ZONE_MARK || info->zone)
+ ct_print_zone_id("--zone", info->zone, info->flags);
}
static void ct_save_v1(const void *ip, const struct xt_entry_target *target)
@@ -256,8 +315,8 @@ static void ct_save_v1(const void *ip, const struct xt_entry_target *target)
if (info->exp_events)
ct_print_events("--expevents", exp_event_tbl,
ARRAY_SIZE(exp_event_tbl), info->exp_events);
- if (info->zone)
- printf(" --zone %u", info->zone);
+ if (info->flags & XT_CT_ZONE_MARK || info->zone)
+ ct_print_zone_id("--zone", info->zone, info->flags);
}
static const char *
diff --git a/extensions/libxt_CT.man b/extensions/libxt_CT.man
index a93eb149..e992120a 100644
--- a/extensions/libxt_CT.man
+++ b/extensions/libxt_CT.man
@@ -20,9 +20,21 @@ the ctmark, not nfmark), \fBnatseqinfo\fP, \fBsecmark\fP (ctsecmark).
Only generate the specified expectation events for this connection.
Possible event types are: \fBnew\fP.
.TP
-\fB\-\-zone\fP \fIid\fP
+\fB\-\-zone-orig\fP {\fIid\fP|\fBmark\fP}
+For traffic coming from ORIGINAL direction, assign this packet to zone
+\fIid\fP and only have lookups done in that zone. If \fBmark\fP is used
+instead of \fIid\fP, the zone is derived from the packet nfmark.
+.TP
+\fB\-\-zone-reply\fP {\fIid\fP|\fBmark\fP}
+For traffic coming from REPLY direction, assign this packet to zone
+\fIid\fP and only have lookups done in that zone. If \fBmark\fP is used
+instead of \fIid\fP, the zone is derived from the packet nfmark.
+.TP
+\fB\-\-zone\fP {\fIid\fP|\fBmark\fP}
Assign this packet to zone \fIid\fP and only have lookups done in that zone.
-By default, packets have zone 0.
+If \fBmark\fP is used instead of \fIid\fP, the zone is derived from the
+packet nfmark. By default, packets have zone 0. This option applies to both
+directions.
.TP
\fB\-\-timeout\fP \fIname\fP
Use the timeout policy identified by \fIname\fP for the connection. This is
diff --git a/extensions/libxt_CT.t b/extensions/libxt_CT.t
new file mode 100644
index 00000000..3c28534e
--- /dev/null
+++ b/extensions/libxt_CT.t
@@ -0,0 +1,20 @@
+:PREROUTING,OUTPUT
+*raw
+-j CT --notrack;=;OK
+-j CT --ctevents new,related,destroy,reply,assured,protoinfo,helper,mark;=;OK
+-j CT --expevents new;=;OK
+# ERROR: cannot find: iptables -I PREROUTING -t raw -j CT --zone 0
+# -j CT --zone 0;=;OK
+-j CT --zone 65535;=;OK
+-j CT --zone 65536;;FAIL
+-j CT --zone -1;;FAIL
+# ERROR: should fail: iptables -A PREROUTING -t raw -j CT
+# -j CT;;FAIL
+@nfct timeout add test inet tcp ESTABLISHED 100
+# cannot load: iptables -A PREROUTING -t raw -j CT --timeout test
+# -j CT --timeout test;=;OK
+@nfct timeout del test
+@nfct helper add rpc inet tcp
+# cannot load: iptables -A PREROUTING -t raw -j CT --helper rpc
+# -j CT --helper rpc;=;OK
+@nfct helper del rpc
diff --git a/extensions/libxt_DSCP.c b/extensions/libxt_DSCP.c
index e16e93c4..cae0d830 100644
--- a/extensions/libxt_DSCP.c
+++ b/extensions/libxt_DSCP.c
@@ -92,21 +92,59 @@ static void DSCP_save(const void *ip, const struct xt_entry_target *target)
printf(" --set-dscp 0x%02x", dinfo->dscp);
}
-static struct xtables_target dscp_target = {
- .family = NFPROTO_UNSPEC,
- .name = "DSCP",
- .version = XTABLES_VERSION,
- .size = XT_ALIGN(sizeof(struct xt_DSCP_info)),
- .userspacesize = XT_ALIGN(sizeof(struct xt_DSCP_info)),
- .help = DSCP_help,
- .print = DSCP_print,
- .save = DSCP_save,
- .x6_parse = DSCP_parse,
- .x6_fcheck = DSCP_check,
- .x6_options = DSCP_opts,
+
+static int DSCP_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_DSCP_info *dinfo =
+ (struct xt_DSCP_info *)params->target->data;
+
+ xt_xlate_add(xl, "ip dscp set 0x%02x", dinfo->dscp);
+ return 1;
+}
+
+static int DSCP_xlate6(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_DSCP_info *dinfo =
+ (struct xt_DSCP_info *)params->target->data;
+
+ xt_xlate_add(xl, "ip6 dscp set 0x%02x", dinfo->dscp);
+ return 1;
+}
+
+static struct xtables_target dscp_target[] = {
+ {
+ .family = NFPROTO_IPV4,
+ .name = "DSCP",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_DSCP_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_DSCP_info)),
+ .help = DSCP_help,
+ .print = DSCP_print,
+ .save = DSCP_save,
+ .x6_parse = DSCP_parse,
+ .x6_fcheck = DSCP_check,
+ .x6_options = DSCP_opts,
+ .xlate = DSCP_xlate,
+ },
+ {
+ .family = NFPROTO_IPV6,
+ .name = "DSCP",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_DSCP_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_DSCP_info)),
+ .help = DSCP_help,
+ .print = DSCP_print,
+ .save = DSCP_save,
+ .x6_parse = DSCP_parse,
+ .x6_fcheck = DSCP_check,
+ .x6_options = DSCP_opts,
+ .xlate = DSCP_xlate6,
+ },
};
void _init(void)
{
- xtables_register_target(&dscp_target);
+ xtables_register_targets(dscp_target, ARRAY_SIZE(dscp_target));
}
diff --git a/extensions/libxt_DSCP.t b/extensions/libxt_DSCP.t
new file mode 100644
index 00000000..fcc55986
--- /dev/null
+++ b/extensions/libxt_DSCP.t
@@ -0,0 +1,11 @@
+:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j DSCP --set-dscp 0;=;OK
+-j DSCP --set-dscp 0x3f;=;OK
+-j DSCP --set-dscp -1;;FAIL
+-j DSCP --set-dscp 0x40;;FAIL
+-j DSCP --set-dscp 0x3f --set-dscp-class CS0;;FAIL
+-j DSCP --set-dscp-class CS0;-j DSCP --set-dscp 0x00;OK
+-j DSCP --set-dscp-class BE;-j DSCP --set-dscp 0x00;OK
+-j DSCP --set-dscp-class EF;-j DSCP --set-dscp 0x2e;OK
+-j DSCP;;FAIL
diff --git a/extensions/libxt_HMARK.t b/extensions/libxt_HMARK.t
new file mode 100644
index 00000000..3bcf1dad
--- /dev/null
+++ b/extensions/libxt_HMARK.t
@@ -0,0 +1,8 @@
+:INPUT,FORWARD,OUTPUT
+-j HMARK;;FAIL
+-j HMARK --hmark-src-prefix 32 --hmark-rnd 0x00000004 --hmark-mod 42;=;OK
+-j HMARK --hmark-src-prefix 32 --hmark-dst-prefix 32 --hmark-sport-mask 0xffff --hmark-dport-mask 0xffff --hmark-proto-mask 0xffff --hmark-rnd 0x00000004 --hmark-mod 42 --hmark-offset 1 --hmark-tuple ct;=;OK
+-j HMARK --hmark-src-prefix 32 --hmark-dst-prefix 32 --hmark-spi-mask 0x00000004 --hmark-proto-mask 0xffff --hmark-rnd 0x00000004 --hmark-mod 42 --hmark-offset 1 --hmark-tuple ct;=;OK
+-j HMARK --hmark-src-prefix 1 --hmark-dst-prefix 2 --hmark-sport-mask 0x0003 --hmark-dport-mask 0x0004 --hmark-proto-mask 0x05 --hmark-rnd 0x00000004 --hmark-mod 42 --hmark-offset 1 --hmark-tuple ct;=;OK
+# cannot mix in spi mask:
+-j HMARK --hmark-src-prefix 32 --hmark-dst-prefix 32 --hmark-sport-mask 0xffff --hmark-dport-mask 0xffff --hmark-proto-mask 0xffff --hmark-rnd 0x00000004 --hmark-mod 42 --hmark-offset 1 --hmark-tuple ct --hmark-spi-mask 4;;FAIL
diff --git a/extensions/libxt_IDLETIMER.t b/extensions/libxt_IDLETIMER.t
new file mode 100644
index 00000000..6afd92c1
--- /dev/null
+++ b/extensions/libxt_IDLETIMER.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD,OUTPUT
+-j IDLETIMER --timeout;;FAIL
+-j IDLETIMER --timeout 42;;FAIL
+-j IDLETIMER --timeout 42 --label foo;=;OK
diff --git a/extensions/libxt_LED.t b/extensions/libxt_LED.t
new file mode 100644
index 00000000..1f6705f4
--- /dev/null
+++ b/extensions/libxt_LED.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD,OUTPUT
+-j LED;;FAIL
+-j LED --led-trigger-id "foo";=;OK
+-j LED --led-trigger-id "foo" --led-delay 42 --led-always-blink;=;OK
diff --git a/extensions/libxt_LOG.man b/extensions/libxt_LOG.man
index 6d3a83a4..354edf4c 100644
--- a/extensions/libxt_LOG.man
+++ b/extensions/libxt_LOG.man
@@ -1,10 +1,8 @@
Turn on kernel logging of matching packets. When this option is set
for a rule, the Linux kernel will print some information on all
matching packets (like most IP/IPv6 header fields) via the kernel log
-(where it can be read with
-.I dmesg
-or
-.IR syslogd (8)).
+(where it can be read with \fIdmesg(1)\fP or read in the syslog).
+.PP
This is a "non-terminating target", i.e. rule traversal continues at
the next rule. So if you want to LOG the packets you refuse, use two
separate rules with the same matching criteria, first using target LOG
diff --git a/extensions/libxt_MARK.c b/extensions/libxt_MARK.c
index 556dbde5..c2f15e3b 100644
--- a/extensions/libxt_MARK.c
+++ b/extensions/libxt_MARK.c
@@ -195,7 +195,7 @@ static void MARK_print_v1(const void *ip, const struct xt_entry_target *target,
case XT_MARK_AND:
printf(" MARK and");
break;
- case XT_MARK_OR:
+ case XT_MARK_OR:
printf(" MARK or");
break;
}
@@ -231,7 +231,7 @@ static void MARK_save_v1(const void *ip, const struct xt_entry_target *target)
case XT_MARK_AND:
printf(" --and-mark");
break;
- case XT_MARK_OR:
+ case XT_MARK_OR:
printf(" --or-mark");
break;
}
@@ -245,6 +245,51 @@ static void mark_tg_save(const void *ip, const struct xt_entry_target *target)
printf(" --set-xmark 0x%x/0x%x", info->mark, info->mask);
}
+static int mark_tg_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_mark_tginfo2 *info = (const void *)params->target->data;
+
+ xt_xlate_add(xl, "meta mark set ");
+
+ if (info->mark == 0)
+ xt_xlate_add(xl, "mark and 0x%x ", ~info->mask);
+ else if (info->mark == info->mask)
+ xt_xlate_add(xl, "mark or 0x%x ", info->mark);
+ else if (info->mask == 0)
+ xt_xlate_add(xl, "mark xor 0x%x ", info->mark);
+ else if (info->mask == 0xffffffffU)
+ xt_xlate_add(xl, "0x%x ", info->mark);
+ else
+ xt_xlate_add(xl, "mark and 0x%x xor 0x%x ", ~info->mask,
+ info->mark);
+
+ return 1;
+}
+
+static int MARK_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_mark_target_info_v1 *markinfo =
+ (const struct xt_mark_target_info_v1 *)params->target->data;
+
+ xt_xlate_add(xl, "meta mark set ");
+
+ switch(markinfo->mode) {
+ case XT_MARK_SET:
+ xt_xlate_add(xl, "0x%x ", markinfo->mark);
+ break;
+ case XT_MARK_AND:
+ xt_xlate_add(xl, "mark and 0x%x ", markinfo->mark);
+ break;
+ case XT_MARK_OR:
+ xt_xlate_add(xl, "mark or 0x%x ", markinfo->mark);
+ break;
+ }
+
+ return 1;
+}
+
static struct xtables_target mark_tg_reg[] = {
{
.family = NFPROTO_UNSPEC,
@@ -273,6 +318,7 @@ static struct xtables_target mark_tg_reg[] = {
.x6_parse = MARK_parse_v1,
.x6_fcheck = MARK_check,
.x6_options = MARK_opts,
+ .xlate = MARK_xlate,
},
{
.version = XTABLES_VERSION,
@@ -287,6 +333,7 @@ static struct xtables_target mark_tg_reg[] = {
.x6_parse = mark_tg_parse,
.x6_fcheck = mark_tg_check,
.x6_options = mark_tg_opts,
+ .xlate = mark_tg_xlate,
},
};
diff --git a/extensions/libxt_MARK.t b/extensions/libxt_MARK.t
new file mode 100644
index 00000000..9d1aa7d7
--- /dev/null
+++ b/extensions/libxt_MARK.t
@@ -0,0 +1,7 @@
+:INPUT,FORWARD,OUTPUT
+-j MARK --set-xmark 0xfeedcafe/0xfeedcafe;=;OK
+-j MARK --set-xmark 0;=;OK
+-j MARK --set-xmark 4294967295;-j MARK --set-xmark 0xffffffff;OK
+-j MARK --set-xmark 4294967296;;FAIL
+-j MARK --set-xmark -1;;FAIL
+-j MARK;;FAIL
diff --git a/extensions/libxt_NFLOG.c b/extensions/libxt_NFLOG.c
index 448576af..02a1b4aa 100644
--- a/extensions/libxt_NFLOG.c
+++ b/extensions/libxt_NFLOG.c
@@ -12,7 +12,10 @@ enum {
O_GROUP = 0,
O_PREFIX,
O_RANGE,
+ O_SIZE,
O_THRESHOLD,
+ F_RANGE = 1 << O_RANGE,
+ F_SIZE = 1 << O_SIZE,
};
#define s struct xt_nflog_info
@@ -22,7 +25,9 @@ static const struct xt_option_entry NFLOG_opts[] = {
{.name = "nflog-prefix", .id = O_PREFIX, .type = XTTYPE_STRING,
.min = 1, .flags = XTOPT_PUT, XTOPT_POINTER(s, prefix)},
{.name = "nflog-range", .id = O_RANGE, .type = XTTYPE_UINT32,
- .flags = XTOPT_PUT, XTOPT_POINTER(s, len)},
+ .excl = F_SIZE, .flags = XTOPT_PUT, XTOPT_POINTER(s, len)},
+ {.name = "nflog-size", .id = O_SIZE, .type = XTTYPE_UINT32,
+ .excl = F_RANGE, .flags = XTOPT_PUT, XTOPT_POINTER(s, len)},
{.name = "nflog-threshold", .id = O_THRESHOLD, .type = XTTYPE_UINT16,
.flags = XTOPT_PUT, XTOPT_POINTER(s, threshold)},
XTOPT_TABLEEND,
@@ -33,7 +38,8 @@ static void NFLOG_help(void)
{
printf("NFLOG target options:\n"
" --nflog-group NUM NETLINK group used for logging\n"
- " --nflog-range NUM Number of byte to copy\n"
+ " --nflog-range NUM This option has no effect, use --nflog-size\n"
+ " --nflog-size NUM Number of bytes to copy\n"
" --nflog-threshold NUM Message threshold of in-kernel queue\n"
" --nflog-prefix STRING Prefix string for log messages\n");
}
@@ -57,6 +63,18 @@ static void NFLOG_parse(struct xt_option_call *cb)
}
}
+static void NFLOG_check(struct xt_fcheck_call *cb)
+{
+ struct xt_nflog_info *info = cb->data;
+
+ if (cb->xflags & F_RANGE)
+ fprintf(stderr, "warn: --nflog-range has never worked and is no"
+ " longer supported, please use --nflog-size insted\n");
+
+ if (cb->xflags & F_SIZE)
+ info->flags |= XT_NFLOG_F_COPY_LEN;
+}
+
static void nflog_print(const struct xt_nflog_info *info, char *prefix)
{
if (info->prefix[0] != '\0') {
@@ -65,14 +83,16 @@ static void nflog_print(const struct xt_nflog_info *info, char *prefix)
}
if (info->group)
printf(" %snflog-group %u", prefix, info->group);
- if (info->len)
+ if (info->flags & XT_NFLOG_F_COPY_LEN)
+ printf(" %snflog-size %u", prefix, info->len);
+ else if (info->len)
printf(" %snflog-range %u", prefix, info->len);
if (info->threshold != XT_NFLOG_DEFAULT_THRESHOLD)
printf(" %snflog-threshold %u", prefix, info->threshold);
}
static void NFLOG_print(const void *ip, const struct xt_entry_target *target,
- int numeric)
+ int numeric)
{
const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data;
@@ -86,6 +106,35 @@ static void NFLOG_save(const void *ip, const struct xt_entry_target *target)
nflog_print(info, "--");
}
+static void nflog_print_xlate(const struct xt_nflog_info *info,
+ struct xt_xlate *xl, bool escape_quotes)
+{
+ xt_xlate_add(xl, "log ");
+ if (info->prefix[0] != '\0') {
+ if (escape_quotes)
+ xt_xlate_add(xl, "prefix \\\"%s\\\" ", info->prefix);
+ else
+ xt_xlate_add(xl, "prefix \"%s\" ", info->prefix);
+
+ }
+ if (info->flags & XT_NFLOG_F_COPY_LEN)
+ xt_xlate_add(xl, "snaplen %u ", info->len);
+ if (info->threshold != XT_NFLOG_DEFAULT_THRESHOLD)
+ xt_xlate_add(xl, "queue-threshold %u ", info->threshold);
+ xt_xlate_add(xl, "group %u ", info->group);
+}
+
+static int NFLOG_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_nflog_info *info =
+ (struct xt_nflog_info *)params->target->data;
+
+ nflog_print_xlate(info, xl, params->escape_quotes);
+
+ return 1;
+}
+
static struct xtables_target nflog_target = {
.family = NFPROTO_UNSPEC,
.name = "NFLOG",
@@ -94,10 +143,12 @@ static struct xtables_target nflog_target = {
.userspacesize = XT_ALIGN(sizeof(struct xt_nflog_info)),
.help = NFLOG_help,
.init = NFLOG_init,
+ .x6_fcheck = NFLOG_check,
.x6_parse = NFLOG_parse,
.print = NFLOG_print,
.save = NFLOG_save,
.x6_options = NFLOG_opts,
+ .xlate = NFLOG_xlate,
};
void _init(void)
diff --git a/extensions/libxt_NFLOG.man b/extensions/libxt_NFLOG.man
index 1b6dbf16..318e6305 100644
--- a/extensions/libxt_NFLOG.man
+++ b/extensions/libxt_NFLOG.man
@@ -17,6 +17,9 @@ A prefix string to include in the log message, up to 64 characters
long, useful for distinguishing messages in the logs.
.TP
\fB\-\-nflog\-range\fP \fIsize\fP
+This option has never worked, use --nflog-size instead
+.TP
+\fB\-\-nflog\-size\fP \fIsize\fP
The number of bytes to be copied to userspace (only applicable for
nfnetlink_log). nfnetlink_log instances may specify their own
range, this option overrides it.
diff --git a/extensions/libxt_NFLOG.t b/extensions/libxt_NFLOG.t
new file mode 100644
index 00000000..933fa221
--- /dev/null
+++ b/extensions/libxt_NFLOG.t
@@ -0,0 +1,24 @@
+:INPUT,FORWARD,OUTPUT
+-j NFLOG --nflog-group 1;=;OK
+-j NFLOG --nflog-group 65535;=;OK
+-j NFLOG --nflog-group 65536;;FAIL
+-j NFLOG --nflog-group 0;-j NFLOG;OK
+-j NFLOG --nflog-range 1;=;OK
+-j NFLOG --nflog-range 4294967295;=;OK
+-j NFLOG --nflog-range 4294967296;;FAIL
+-j NFLOG --nflog-range -1;;FAIL
+-j NFLOG --nflog-size 0;=;OK
+-j NFLOG --nflog-size 1;=;OK
+-j NFLOG --nflog-size 4294967295;=;OK
+-j NFLOG --nflog-size 4294967296;;FAIL
+-j NFLOG --nflog-size -1;;FAIL
+# ERROR: cannot find: iptables -I INPUT -j NFLOG --nflog-prefix xxxxxx [...]
+# -j NFLOG --nflog-prefix xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;=;OK
+# ERROR: should fail: iptables -A INPUT -j NFLOG --nflog-prefix xxxxxxx [...]
+# -j NFLOG --nflog-prefix xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;;FAIL
+-j NFLOG --nflog-threshold 1;=;OK
+# ERROR: line 13 (should fail: iptables -A INPUT -j NFLOG --nflog-threshold 0
+# -j NFLOG --nflog-threshold 0;;FAIL
+-j NFLOG --nflog-threshold 65535;=;OK
+-j NFLOG --nflog-threshold 65536;;FAIL
+-j NFLOG;=;OK
diff --git a/extensions/libxt_NFQUEUE.c b/extensions/libxt_NFQUEUE.c
index 0c869187..fe519078 100644
--- a/extensions/libxt_NFQUEUE.c
+++ b/extensions/libxt_NFQUEUE.c
@@ -30,23 +30,32 @@ static void NFQUEUE_help(void)
static void NFQUEUE_help_v1(void)
{
- NFQUEUE_help();
printf(
+"NFQUEUE target options\n"
+" --queue-num value Send packet to QUEUE number <value>.\n"
+" Valid queue numbers are 0-65535\n"
" --queue-balance first:last Balance flows between queues <value> to <value>.\n");
}
static void NFQUEUE_help_v2(void)
{
- NFQUEUE_help_v1();
printf(
+"NFQUEUE target options\n"
+" --queue-num value Send packet to QUEUE number <value>.\n"
+" Valid queue numbers are 0-65535\n"
+" --queue-balance first:last Balance flows between queues <value> to <value>.\n"
" --queue-bypass Bypass Queueing if no queue instance exists.\n"
" --queue-cpu-fanout Use current CPU (no hashing)\n");
}
static void NFQUEUE_help_v3(void)
{
- NFQUEUE_help_v2();
printf(
+"NFQUEUE target options\n"
+" --queue-num value Send packet to QUEUE number <value>.\n"
+" Valid queue numbers are 0-65535\n"
+" --queue-balance first:last Balance flows between queues <value> to <value>.\n"
+" --queue-bypass Bypass Queueing if no queue instance exists.\n"
" --queue-cpu-fanout Use current CPU (no hashing)\n");
}
@@ -95,11 +104,23 @@ static void NFQUEUE_parse_v1(struct xt_option_call *cb)
static void NFQUEUE_parse_v2(struct xt_option_call *cb)
{
struct xt_NFQ_info_v2 *info = cb->data;
+ const uint16_t *r = cb->val.u16_range;
- NFQUEUE_parse_v1(cb);
+ xtables_option_parse(cb);
switch (cb->entry->id) {
+ case O_QUEUE_BALANCE:
+ if (cb->nvals != 2)
+ xtables_error(PARAMETER_PROBLEM,
+ "Bad range \"%s\"", cb->arg);
+ if (r[0] >= r[1])
+ xtables_error(PARAMETER_PROBLEM,
+ "%u should be less than %u",
+ r[0], r[1]);
+ info->queuenum = r[0];
+ info->queues_total = r[1] - r[0] + 1;
+ break;
case O_QUEUE_BYPASS:
- info->bypass = 1;
+ info->bypass |= NFQ_FLAG_BYPASS;
break;
}
}
@@ -107,9 +128,24 @@ static void NFQUEUE_parse_v2(struct xt_option_call *cb)
static void NFQUEUE_parse_v3(struct xt_option_call *cb)
{
struct xt_NFQ_info_v3 *info = cb->data;
+ const uint16_t *r = cb->val.u16_range;
- NFQUEUE_parse_v2(cb);
+ xtables_option_parse(cb);
switch (cb->entry->id) {
+ case O_QUEUE_BALANCE:
+ if (cb->nvals != 2)
+ xtables_error(PARAMETER_PROBLEM,
+ "Bad range \"%s\"", cb->arg);
+ if (r[0] >= r[1])
+ xtables_error(PARAMETER_PROBLEM,
+ "%u should be less than %u",
+ r[0], r[1]);
+ info->queuenum = r[0];
+ info->queues_total = r[1] - r[0] + 1;
+ break;
+ case O_QUEUE_BYPASS:
+ info->flags |= NFQ_FLAG_BYPASS;
+ break;
case O_QUEUE_CPU_FANOUT:
info->flags |= NFQ_FLAG_CPU_FANOUT;
break;
@@ -142,8 +178,14 @@ static void NFQUEUE_print_v2(const void *ip,
const struct xt_entry_target *target, int numeric)
{
const struct xt_NFQ_info_v2 *info = (void *) target->data;
+ unsigned int last = info->queues_total;
+
+ if (last > 1) {
+ last += info->queuenum - 1;
+ printf(" NFQUEUE balance %u:%u", info->queuenum, last);
+ } else
+ printf(" NFQUEUE num %u", info->queuenum);
- NFQUEUE_print_v1(ip, target, numeric);
if (info->bypass & NFQ_FLAG_BYPASS)
printf(" bypass");
}
@@ -152,8 +194,17 @@ static void NFQUEUE_print_v3(const void *ip,
const struct xt_entry_target *target, int numeric)
{
const struct xt_NFQ_info_v3 *info = (void *)target->data;
+ unsigned int last = info->queues_total;
+
+ if (last > 1) {
+ last += info->queuenum - 1;
+ printf(" NFQUEUE balance %u:%u", info->queuenum, last);
+ } else
+ printf(" NFQUEUE num %u", info->queuenum);
+
+ if (info->flags & NFQ_FLAG_BYPASS)
+ printf(" bypass");
- NFQUEUE_print_v2(ip, target, numeric);
if (info->flags & NFQ_FLAG_CPU_FANOUT)
printf(" cpu-fanout");
}
@@ -182,8 +233,13 @@ static void NFQUEUE_save_v1(const void *ip, const struct xt_entry_target *target
static void NFQUEUE_save_v2(const void *ip, const struct xt_entry_target *target)
{
const struct xt_NFQ_info_v2 *info = (void *) target->data;
+ unsigned int last = info->queues_total;
- NFQUEUE_save_v1(ip, target);
+ if (last > 1) {
+ last += info->queuenum - 1;
+ printf(" --queue-balance %u:%u", info->queuenum, last);
+ } else
+ printf(" --queue-num %u", info->queuenum);
if (info->bypass & NFQ_FLAG_BYPASS)
printf(" --queue-bypass");
@@ -193,8 +249,17 @@ static void NFQUEUE_save_v3(const void *ip,
const struct xt_entry_target *target)
{
const struct xt_NFQ_info_v3 *info = (void *)target->data;
+ unsigned int last = info->queues_total;
+
+ if (last > 1) {
+ last += info->queuenum - 1;
+ printf(" --queue-balance %u:%u", info->queuenum, last);
+ } else
+ printf(" --queue-num %u", info->queuenum);
+
+ if (info->flags & NFQ_FLAG_BYPASS)
+ printf(" --queue-bypass");
- NFQUEUE_save_v2(ip, target);
if (info->flags & NFQ_FLAG_CPU_FANOUT)
printf(" --queue-cpu-fanout");
}
@@ -205,6 +270,73 @@ static void NFQUEUE_init_v1(struct xt_entry_target *t)
tinfo->queues_total = 1;
}
+static int NFQUEUE_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_NFQ_info *tinfo =
+ (const struct xt_NFQ_info *)params->target->data;
+
+ xt_xlate_add(xl, "queue num %u ", tinfo->queuenum);
+
+ return 1;
+}
+
+static int NFQUEUE_xlate_v1(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_NFQ_info_v1 *tinfo = (const void *)params->target->data;
+ unsigned int last = tinfo->queues_total;
+
+ if (last > 1) {
+ last += tinfo->queuenum - 1;
+ xt_xlate_add(xl, "queue num %u-%u ", tinfo->queuenum, last);
+ } else {
+ xt_xlate_add(xl, "queue num %u ", tinfo->queuenum);
+ }
+
+ return 1;
+}
+
+static int NFQUEUE_xlate_v2(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_NFQ_info_v2 *info = (void *)params->target->data;
+ unsigned int last = info->queues_total;
+
+ if (last > 1) {
+ last += info->queuenum - 1;
+ xt_xlate_add(xl, "queue num %u-%u ", info->queuenum, last);
+ } else
+ xt_xlate_add(xl, "queue num %u ", info->queuenum);
+
+ if (info->bypass & NFQ_FLAG_BYPASS)
+ xt_xlate_add(xl, "bypass");
+
+ return 1;
+}
+
+static int NFQUEUE_xlate_v3(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_NFQ_info_v3 *info = (void *)params->target->data;
+ unsigned int last = info->queues_total;
+
+ if (last > 1) {
+ last += info->queuenum - 1;
+ xt_xlate_add(xl, "queue num %u-%u ", info->queuenum, last);
+ } else
+ xt_xlate_add(xl, "queue num %u ", info->queuenum);
+
+ if (info->flags & NFQ_FLAG_BYPASS)
+ xt_xlate_add(xl, "bypass");
+
+ if (info->flags & NFQ_FLAG_CPU_FANOUT)
+ xt_xlate_add(xl, "%sfanout ",
+ info->flags & NFQ_FLAG_BYPASS ? "," : "");
+
+ return 1;
+}
+
static struct xtables_target nfqueue_targets[] = {
{
.family = NFPROTO_UNSPEC,
@@ -216,7 +348,8 @@ static struct xtables_target nfqueue_targets[] = {
.print = NFQUEUE_print,
.save = NFQUEUE_save,
.x6_parse = NFQUEUE_parse,
- .x6_options = NFQUEUE_opts
+ .x6_options = NFQUEUE_opts,
+ .xlate = NFQUEUE_xlate,
},{
.family = NFPROTO_UNSPEC,
.revision = 1,
@@ -230,6 +363,7 @@ static struct xtables_target nfqueue_targets[] = {
.save = NFQUEUE_save_v1,
.x6_parse = NFQUEUE_parse_v1,
.x6_options = NFQUEUE_opts,
+ .xlate = NFQUEUE_xlate_v1,
},{
.family = NFPROTO_UNSPEC,
.revision = 2,
@@ -243,6 +377,7 @@ static struct xtables_target nfqueue_targets[] = {
.save = NFQUEUE_save_v2,
.x6_parse = NFQUEUE_parse_v2,
.x6_options = NFQUEUE_opts,
+ .xlate = NFQUEUE_xlate_v2,
},{
.family = NFPROTO_UNSPEC,
.revision = 3,
@@ -256,6 +391,7 @@ static struct xtables_target nfqueue_targets[] = {
.save = NFQUEUE_save_v3,
.x6_parse = NFQUEUE_parse_v3,
.x6_options = NFQUEUE_opts,
+ .xlate = NFQUEUE_xlate_v3,
}
};
diff --git a/extensions/libxt_NFQUEUE.man b/extensions/libxt_NFQUEUE.man
index 7a991291..1bfb7b84 100644
--- a/extensions/libxt_NFQUEUE.man
+++ b/extensions/libxt_NFQUEUE.man
@@ -1,11 +1,12 @@
-This target is an extension of the QUEUE target. As opposed to QUEUE, it allows
-you to put a packet into any specific queue, identified by its 16-bit queue
-number.
-It can only be used with Kernel versions 2.6.14 or later, since it requires
-the
+This target passes the packet to userspace using the
+\fBnfnetlink_queue\fP handler. The packet is put into the queue
+identified by its 16-bit queue number. Userspace can inspect
+and modify the packet if desired. Userspace must then drop or
+reinject the packet into the kernel. Please see libnetfilter_queue
+for details.
.B
nfnetlink_queue
-kernel support. The \fBqueue-balance\fP option was added in Linux 2.6.31,
+was added in Linux 2.6.14. The \fBqueue-balance\fP option was added in Linux 2.6.31,
\fBqueue-bypass\fP in 2.6.39.
.TP
\fB\-\-queue\-num\fP \fIvalue\fP
diff --git a/extensions/libxt_NFQUEUE.t b/extensions/libxt_NFQUEUE.t
new file mode 100644
index 00000000..b51b19fd
--- /dev/null
+++ b/extensions/libxt_NFQUEUE.t
@@ -0,0 +1,16 @@
+:INPUT,FORWARD,OUTPUT
+-j NFQUEUE;=;OK
+-j NFQUEUE --queue-num 0;=;OK
+-j NFQUEUE --queue-num 65535;=;OK
+-j NFQUEUE --queue-num 65536;;FAIL
+-j NFQUEUE --queue-num -1;;FAIL
+# it says "NFQUEUE: number of total queues is 0", overflow in NFQUEUE_parse_v1?
+# ERROR: cannot load: iptables -A INPUT -j NFQUEUE --queue-balance 0:65535
+# -j NFQUEUE --queue-balance 0:65535;=;OK
+-j NFQUEUE --queue-balance 0:65536;;FAIL
+-j NFQUEUE --queue-balance -1:65535;;FAIL
+-j NFQUEUE --queue-num 10 --queue-bypass;=;OK
+-j NFQUEUE --queue-balance 0:6 --queue-cpu-fanout --queue-bypass;-j NFQUEUE --queue-balance 0:6 --queue-bypass --queue-cpu-fanout;OK
+-j NFQUEUE --queue-bypass --queue-balance 0:6 --queue-cpu-fanout;-j NFQUEUE --queue-balance 0:6 --queue-bypass --queue-cpu-fanout;OK
+-j NFQUEUE --queue-balance 0:6 --queue-bypass;=;OK
+-j NFQUEUE --queue-bypass;-j NFQUEUE --queue-num 0 --queue-bypass;OK
diff --git a/extensions/libxt_NOTRACK.t b/extensions/libxt_NOTRACK.t
new file mode 100644
index 00000000..585be82d
--- /dev/null
+++ b/extensions/libxt_NOTRACK.t
@@ -0,0 +1,4 @@
+:PREROUTING,OUTPUT
+*raw
+# ERROR: cannot find: iptables -I PREROUTING -t raw -j NOTRACK
+#-j NOTRACK;=;OK
diff --git a/extensions/libxt_RATEEST.t b/extensions/libxt_RATEEST.t
new file mode 100644
index 00000000..c2b6bb34
--- /dev/null
+++ b/extensions/libxt_RATEEST.t
@@ -0,0 +1,2 @@
+:INPUT,FORWARD,OUTPUT
+-j RATEEST --rateest-name RE1 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms;=;OK
diff --git a/extensions/libxt_SET.c b/extensions/libxt_SET.c
index a11db395..2a7640a0 100644
--- a/extensions/libxt_SET.c
+++ b/extensions/libxt_SET.c
@@ -5,7 +5,7 @@
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * published by the Free Software Foundation.
*/
/* Shared library add-on to iptables to add IP set mangling target. */
@@ -80,7 +80,7 @@ parse_target_v0(char **argv, int invert, unsigned int *flags,
get_set_byname(optarg, (struct xt_set_info *)info);
parse_dirs_v0(argv[optind], info);
optind++;
-
+
*flags = 1;
}
@@ -116,7 +116,7 @@ print_target_v0(const char *prefix, const struct xt_set_info_v0 *info)
printf(" %s %s", prefix, setname);
for (i = 0; i < IPSET_DIM_MAX; i++) {
if (!info->u.flags[i])
- break;
+ break;
printf("%s%s",
i == 0 ? " " : ",",
info->u.flags[i] & IPSET_SRC ? "src" : "dst");
@@ -125,7 +125,7 @@ print_target_v0(const char *prefix, const struct xt_set_info_v0 *info)
static void
set_target_print_v0(const void *ip, const struct xt_entry_target *target,
- int numeric)
+ int numeric)
{
const struct xt_set_info_target_v0 *info = (const void *)target->data;
@@ -158,6 +158,10 @@ set_target_init_v1(struct xt_entry_target *target)
#define SET_TARGET_DEL 0x2
#define SET_TARGET_EXIST 0x4
#define SET_TARGET_TIMEOUT 0x8
+#define SET_TARGET_MAP 0x10
+#define SET_TARGET_MAP_MARK 0x20
+#define SET_TARGET_MAP_PRIO 0x40
+#define SET_TARGET_MAP_QUEUE 0x80
static void
parse_target(char **argv, int invert, struct xt_set_info *info,
@@ -314,7 +318,7 @@ set_target_parse_v2(int c, char **argv, int invert, unsigned int *flags,
"or out of range 0-%u", UINT32_MAX - 1);
myinfo->timeout = timeout;
*flags |= SET_TARGET_TIMEOUT;
- break;
+ break;
}
return 1;
}
@@ -346,6 +350,170 @@ set_target_save_v2(const void *ip, const struct xt_entry_target *target)
print_target("--del-set", &info->del_set);
}
+
+/* Revision 3 */
+
+static void
+set_target_help_v3(void)
+{
+ printf("SET target options:\n"
+ " --add-set name flags [--exist] [--timeout n]\n"
+ " --del-set name flags\n"
+ " --map-set name flags"
+ " [--map-mark] [--map-prio] [--map-queue]\n"
+ " add/del src/dst IP/port from/to named sets,\n"
+ " where flags are the comma separated list of\n"
+ " 'src' and 'dst' specifications.\n");
+}
+
+static const struct option set_target_opts_v3[] = {
+ {.name = "add-set", .has_arg = true, .val = '1'},
+ {.name = "del-set", .has_arg = true, .val = '2'},
+ {.name = "exist", .has_arg = false, .val = '3'},
+ {.name = "timeout", .has_arg = true, .val = '4'},
+ {.name = "map-set", .has_arg = true, .val = '5'},
+ {.name = "map-mark", .has_arg = false, .val = '6'},
+ {.name = "map-prio", .has_arg = false, .val = '7'},
+ {.name = "map-queue", .has_arg = false, .val = '8'},
+ XT_GETOPT_TABLEEND,
+};
+
+static void
+set_target_check_v3(unsigned int flags)
+{
+ if (!(flags & (SET_TARGET_ADD|SET_TARGET_DEL|SET_TARGET_MAP)))
+ xtables_error(PARAMETER_PROBLEM,
+ "You must specify either `--add-set' or "
+ "`--del-set' or `--map-set'");
+ if (!(flags & SET_TARGET_ADD)) {
+ if (flags & SET_TARGET_EXIST)
+ xtables_error(PARAMETER_PROBLEM,
+ "Flag `--exist' can be used with `--add-set' only");
+ if (flags & SET_TARGET_TIMEOUT)
+ xtables_error(PARAMETER_PROBLEM,
+ "Option `--timeout' can be used with `--add-set' only");
+ }
+ if (!(flags & SET_TARGET_MAP)) {
+ if (flags & SET_TARGET_MAP_MARK)
+ xtables_error(PARAMETER_PROBLEM,
+ "Flag `--map-mark' can be used with `--map-set' only");
+ if (flags & SET_TARGET_MAP_PRIO)
+ xtables_error(PARAMETER_PROBLEM,
+ "Flag `--map-prio' can be used with `--map-set' only");
+ if (flags & SET_TARGET_MAP_QUEUE)
+ xtables_error(PARAMETER_PROBLEM,
+ "Flag `--map-queue' can be used with `--map-set' only");
+ }
+ if ((flags & SET_TARGET_MAP) && !(flags & (SET_TARGET_MAP_MARK |
+ SET_TARGET_MAP_PRIO |
+ SET_TARGET_MAP_QUEUE)))
+ xtables_error(PARAMETER_PROBLEM,
+ "You must specify flags `--map-mark' or "
+ "'--map-prio` or `--map-queue'");
+}
+
+static void
+set_target_init_v3(struct xt_entry_target *target)
+{
+ struct xt_set_info_target_v3 *info =
+ (struct xt_set_info_target_v3 *) target->data;
+
+ info->add_set.index =
+ info->del_set.index =
+ info->map_set.index = IPSET_INVALID_ID;
+ info->timeout = UINT32_MAX;
+}
+
+static int
+set_target_parse_v3(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **target)
+{
+ struct xt_set_info_target_v3 *myinfo =
+ (struct xt_set_info_target_v3 *) (*target)->data;
+ unsigned int timeout;
+
+ switch (c) {
+ case '1': /* --add-set <set> <flags> */
+ parse_target(argv, invert, &myinfo->add_set, "add-set");
+ *flags |= SET_TARGET_ADD;
+ break;
+ case '2': /* --del-set <set>[:<flags>] <flags> */
+ parse_target(argv, invert, &myinfo->del_set, "del-set");
+ *flags |= SET_TARGET_DEL;
+ break;
+ case '3':
+ myinfo->flags |= IPSET_FLAG_EXIST;
+ *flags |= SET_TARGET_EXIST;
+ break;
+ case '4':
+ if (!xtables_strtoui(optarg, NULL, &timeout, 0, UINT32_MAX - 1))
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid value for option --timeout "
+ "or out of range 0-%u", UINT32_MAX - 1);
+ myinfo->timeout = timeout;
+ *flags |= SET_TARGET_TIMEOUT;
+ break;
+ case '5': /* --map-set <set> <flags> */
+ parse_target(argv, invert, &myinfo->map_set, "map-set");
+ *flags |= SET_TARGET_MAP;
+ break;
+ case '6':
+ myinfo->flags |= IPSET_FLAG_MAP_SKBMARK;
+ *flags |= SET_TARGET_MAP_MARK;
+ break;
+ case '7':
+ myinfo->flags |= IPSET_FLAG_MAP_SKBPRIO;
+ *flags |= SET_TARGET_MAP_PRIO;
+ break;
+ case '8':
+ myinfo->flags |= IPSET_FLAG_MAP_SKBQUEUE;
+ *flags |= SET_TARGET_MAP_QUEUE;
+ break;
+ }
+ return 1;
+}
+
+static void
+set_target_print_v3(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ const struct xt_set_info_target_v3 *info = (const void *)target->data;
+
+ print_target("add-set", &info->add_set);
+ if (info->flags & IPSET_FLAG_EXIST)
+ printf(" exist");
+ if (info->timeout != UINT32_MAX)
+ printf(" timeout %u", info->timeout);
+ print_target("del-set", &info->del_set);
+ print_target("map-set", &info->map_set);
+ if (info->flags & IPSET_FLAG_MAP_SKBMARK)
+ printf(" map-mark");
+ if (info->flags & IPSET_FLAG_MAP_SKBPRIO)
+ printf(" map-prio");
+ if (info->flags & IPSET_FLAG_MAP_SKBQUEUE)
+ printf(" map-queue");
+}
+
+static void
+set_target_save_v3(const void *ip, const struct xt_entry_target *target)
+{
+ const struct xt_set_info_target_v3 *info = (const void *)target->data;
+
+ print_target("--add-set", &info->add_set);
+ if (info->flags & IPSET_FLAG_EXIST)
+ printf(" --exist");
+ if (info->timeout != UINT32_MAX)
+ printf(" --timeout %u", info->timeout);
+ print_target("--del-set", &info->del_set);
+ print_target("--map-set", &info->map_set);
+ if (info->flags & IPSET_FLAG_MAP_SKBMARK)
+ printf(" --map-mark");
+ if (info->flags & IPSET_FLAG_MAP_SKBPRIO)
+ printf(" --map-prio");
+ if (info->flags & IPSET_FLAG_MAP_SKBQUEUE)
+ printf(" --map-queue");
+}
+
static struct xtables_target set_tg_reg[] = {
{
.name = "SET",
@@ -392,6 +560,21 @@ static struct xtables_target set_tg_reg[] = {
.save = set_target_save_v2,
.extra_opts = set_target_opts_v2,
},
+ {
+ .name = "SET",
+ .revision = 3,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_UNSPEC,
+ .size = XT_ALIGN(sizeof(struct xt_set_info_target_v3)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_target_v3)),
+ .help = set_target_help_v3,
+ .init = set_target_init_v3,
+ .parse = set_target_parse_v3,
+ .final_check = set_target_check_v3,
+ .print = set_target_print_v3,
+ .save = set_target_save_v3,
+ .extra_opts = set_target_opts_v3,
+ },
};
void _init(void)
diff --git a/extensions/libxt_SET.man b/extensions/libxt_SET.man
index c35ba93d..78a9ae0f 100644
--- a/extensions/libxt_SET.man
+++ b/extensions/libxt_SET.man
@@ -6,6 +6,10 @@ add the address(es)/port(s) of the packet to the set
.TP
\fB\-\-del\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP...]
delete the address(es)/port(s) of the packet from the set
+.TP
+\fB\-\-map\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP...]
+[\-\-map\-mark] [\-\-map\-prio] [\-\-map\-queue]
+map packet properties (firewall mark, tc priority, hardware queue)
.IP
where \fIflag\fP(s) are
.BR "src"
@@ -20,6 +24,23 @@ one from the set definition
\fB\-\-exist\fP
when adding an entry if it already exists, reset the timeout value
to the specified one or to the default from the set definition
+.TP
+\fB\-\-map\-set\fP \fIset\-name\fP
+the set-name should be created with --skbinfo option
+\fB\-\-map\-mark\fP
+map firewall mark to packet by lookup of value in the set
+\fB\-\-map\-prio\fP
+map traffic control priority to packet by lookup of value in the set
+\fB\-\-map\-queue\fP
+map hardware NIC queue to packet by lookup of value in the set
+.IP
+The
+\fB\-\-map\-set\fP
+option can be used from the mangle table only. The
+\fB\-\-map\-prio\fP
+and
+\fB\-\-map\-queue\fP
+flags can be used in the OUTPUT, FORWARD and POSTROUTING chains.
.PP
Use of -j SET requires that ipset kernel support is provided, which, for
standard kernels, is the case since Linux 2.6.39.
diff --git a/extensions/libxt_SET.t b/extensions/libxt_SET.t
new file mode 100644
index 00000000..30c27ca3
--- /dev/null
+++ b/extensions/libxt_SET.t
@@ -0,0 +1,3 @@
+:INPUT,FORWARD,OUTPUT
+# fails: foo does not exist
+-j SET --add-set foo src,dst;;FAIL
diff --git a/extensions/libxt_SNAT.man b/extensions/libxt_SNAT.man
index f0620a21..8cd0b80e 100644
--- a/extensions/libxt_SNAT.man
+++ b/extensions/libxt_SNAT.man
@@ -29,7 +29,12 @@ anymore.
\fB\-\-random\fP
If option
\fB\-\-random\fP
-is used then port mapping will be randomized (kernel >= 2.6.21).
+is used then port mapping will be randomized through a hash-based algorithm (kernel >= 2.6.21).
+.TP
+\fB\-\-random-fully\fP
+If option
+\fB\-\-random-fully\fP
+is used then port mapping will be fully randomized through a PRNG (kernel >= 3.14).
.TP
\fB\-\-persistent\fP
Gives a client the same source-/destination-address for each connection.
diff --git a/extensions/libxt_SYNPROXY.c b/extensions/libxt_SYNPROXY.c
new file mode 100644
index 00000000..475590ea
--- /dev/null
+++ b/extensions/libxt_SYNPROXY.c
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_SYNPROXY.h>
+
+enum {
+ O_SACK_PERM = 0,
+ O_TIMESTAMP,
+ O_WSCALE,
+ O_MSS,
+ O_ECN,
+};
+
+static void SYNPROXY_help(void)
+{
+ printf(
+"SYNPROXY target options:\n"
+" --sack-perm Set SACK_PERM\n"
+" --timestamp Set TIMESTAMP\n"
+" --wscale value Set window scaling factor\n"
+" --mss value Set MSS value\n"
+" --ecn Set ECN\n");
+}
+
+static const struct xt_option_entry SYNPROXY_opts[] = {
+ {.name = "sack-perm", .id = O_SACK_PERM, .type = XTTYPE_NONE, },
+ {.name = "timestamp", .id = O_TIMESTAMP, .type = XTTYPE_NONE, },
+ {.name = "wscale", .id = O_WSCALE, .type = XTTYPE_UINT32, },
+ {.name = "mss", .id = O_MSS, .type = XTTYPE_UINT32, },
+ {.name = "ecn", .id = O_ECN, .type = XTTYPE_NONE, },
+ XTOPT_TABLEEND,
+};
+
+static void SYNPROXY_parse(struct xt_option_call *cb)
+{
+ struct xt_synproxy_info *info = cb->data;
+
+ xtables_option_parse(cb);
+ switch (cb->entry->id) {
+ case O_SACK_PERM:
+ info->options |= XT_SYNPROXY_OPT_SACK_PERM;
+ break;
+ case O_TIMESTAMP:
+ info->options |= XT_SYNPROXY_OPT_TIMESTAMP;
+ break;
+ case O_WSCALE:
+ info->options |= XT_SYNPROXY_OPT_WSCALE;
+ info->wscale = cb->val.u32;
+ break;
+ case O_MSS:
+ info->options |= XT_SYNPROXY_OPT_MSS;
+ info->mss = cb->val.u32;
+ break;
+ case O_ECN:
+ info->options |= XT_SYNPROXY_OPT_ECN;
+ break;
+ }
+}
+
+static void SYNPROXY_check(struct xt_fcheck_call *cb)
+{
+}
+
+static void SYNPROXY_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ const struct xt_synproxy_info *info =
+ (const struct xt_synproxy_info *)target->data;
+
+ printf(" SYNPROXY ");
+ if (info->options & XT_SYNPROXY_OPT_SACK_PERM)
+ printf("sack-perm ");
+ if (info->options & XT_SYNPROXY_OPT_TIMESTAMP)
+ printf("timestamp ");
+ if (info->options & XT_SYNPROXY_OPT_WSCALE)
+ printf("wscale %u ", info->wscale);
+ if (info->options & XT_SYNPROXY_OPT_MSS)
+ printf("mss %u ", info->mss);
+ if (info->options & XT_SYNPROXY_OPT_ECN)
+ printf("ecn ");
+}
+
+static void SYNPROXY_save(const void *ip, const struct xt_entry_target *target)
+{
+ const struct xt_synproxy_info *info =
+ (const struct xt_synproxy_info *)target->data;
+
+ if (info->options & XT_SYNPROXY_OPT_SACK_PERM)
+ printf(" --sack-perm");
+ if (info->options & XT_SYNPROXY_OPT_TIMESTAMP)
+ printf(" --timestamp");
+ if (info->options & XT_SYNPROXY_OPT_WSCALE)
+ printf(" --wscale %u", info->wscale);
+ if (info->options & XT_SYNPROXY_OPT_MSS)
+ printf(" --mss %u", info->mss);
+ if (info->options & XT_SYNPROXY_OPT_ECN)
+ printf(" --ecn");
+}
+
+static struct xtables_target synproxy_tg_reg = {
+ .family = NFPROTO_UNSPEC,
+ .name = "SYNPROXY",
+ .version = XTABLES_VERSION,
+ .revision = 0,
+ .size = XT_ALIGN(sizeof(struct xt_synproxy_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_synproxy_info)),
+ .help = SYNPROXY_help,
+ .print = SYNPROXY_print,
+ .save = SYNPROXY_save,
+ .x6_parse = SYNPROXY_parse,
+ .x6_fcheck = SYNPROXY_check,
+ .x6_options = SYNPROXY_opts,
+};
+
+void _init(void)
+{
+ xtables_register_target(&synproxy_tg_reg);
+}
diff --git a/extensions/libxt_SYNPROXY.man b/extensions/libxt_SYNPROXY.man
new file mode 100644
index 00000000..25325fc2
--- /dev/null
+++ b/extensions/libxt_SYNPROXY.man
@@ -0,0 +1,64 @@
+This target will process TCP three-way-handshake parallel in netfilter
+context to protect either local or backend system. This target requires
+connection tracking because sequence numbers need to be translated.
+.TP
+\fB\-\-mss\fP \fImaximum segment size\fP
+Maximum segment size announced to clients. This must match the backend.
+.TP
+\fB\-\-wscale\fP \fIwindow scale\fP
+Window scale announced to clients. This must match the backend.
+.TP
+\fB\-\-sack\-perm\fP
+Pass client selective acknowledgement option to backend (will be disabled
+if not present).
+.TP
+\fB\-\-timestamps\fP
+Pass client timestamp option to backend (will be disabled if not present,
+also needed for selective acknowledgement and window scaling).
+.PP
+Example:
+.PP
+Determine tcp options used by backend, from an external system
+.IP
+tcpdump -pni eth0 -c 1 'tcp[tcpflags] == (tcp-syn|tcp-ack)'
+.br
+ port 80 &
+.br
+telnet 192.0.2.42 80
+.br
+18:57:24.693307 IP 192.0.2.42.80 > 192.0.2.43.48757:
+.br
+ Flags [S.], seq 360414582, ack 788841994, win 14480,
+.br
+ options [mss 1460,sackOK,
+.br
+ TS val 1409056151 ecr 9690221,
+.br
+ nop,wscale 9],
+.br
+ length 0
+.PP
+Switch tcp_loose mode off, so conntrack will mark out\-of\-flow
+packets as state INVALID.
+.IP
+echo 0 > /proc/sys/net/netfilter/nf_conntrack_tcp_loose
+.PP
+Make SYN packets untracked
+.IP
+iptables \-t raw \-A PREROUTING \-i eth0 \-p tcp \-\-dport 80
+ \-\-syn \-j CT \-\-notrack
+.PP
+Catch UNTRACKED (SYN packets) and INVALID (3WHS ACK packets) states
+and send them to SYNPROXY. This rule will respond to SYN packets with
+SYN+ACK syncookies, create ESTABLISHED for valid client response (3WHS ACK
+packets) and drop incorrect cookies. Flags combinations not expected
+during 3WHS will not match and continue (e.g. SYN+FIN, SYN+ACK).
+.IP
+iptables \-A INPUT \-i eth0 \-p tcp \-\-dport 80
+ \-m state \-\-state UNTRACKED,INVALID \-j SYNPROXY
+ \-\-sack\-perm \-\-timestamp \-\-mss 1460 \-\-wscale 9
+.PP
+Drop invalid packets, this will be out\-of\-flow packets that were not
+matched by SYNPROXY.
+.IP
+iptables \-A INPUT \-i eth0 \-p tcp \-\-dport 80 \-m state \-\-state INVALID \-j DROP
diff --git a/extensions/libxt_SYNPROXY.t b/extensions/libxt_SYNPROXY.t
new file mode 100644
index 00000000..dd8b0e76
--- /dev/null
+++ b/extensions/libxt_SYNPROXY.t
@@ -0,0 +1,3 @@
+:INPUT,FORWARD
+-j SYNPROXY --sack-perm --timestamp --mss 1460 --wscale 9;;FAIL
+-p tcp -m tcp --dport 42 -m conntrack --ctstate INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 9 --mss 1460;=;OK
diff --git a/extensions/libxt_TCPMSS.t b/extensions/libxt_TCPMSS.t
new file mode 100644
index 00000000..553a3452
--- /dev/null
+++ b/extensions/libxt_TCPMSS.t
@@ -0,0 +1,6 @@
+:FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j TCPMSS;;FAIL
+-p tcp -j TCPMSS --set-mss 42;;FAIL
+-p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j TCPMSS --set-mss 42;=;OK
+-p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j TCPMSS --clamp-mss-to-pmtu;=;OK
diff --git a/extensions/libxt_TCPOPTSTRIP.c b/extensions/libxt_TCPOPTSTRIP.c
index 68978573..6ea34892 100644
--- a/extensions/libxt_TCPOPTSTRIP.c
+++ b/extensions/libxt_TCPOPTSTRIP.c
@@ -12,6 +12,21 @@
#ifndef TCPOPT_MD5SIG
# define TCPOPT_MD5SIG 19
#endif
+#ifndef TCPOPT_MAXSEG
+# define TCPOPT_MAXSEG 2
+#endif
+#ifndef TCPOPT_WINDOW
+# define TCPOPT_WINDOW 3
+#endif
+#ifndef TCPOPT_SACK_PERMITTED
+# define TCPOPT_SACK_PERMITTED 4
+#endif
+#ifndef TCPOPT_SACK
+# define TCPOPT_SACK 5
+#endif
+#ifndef TCPOPT_TIMESTAMP
+# define TCPOPT_TIMESTAMP 8
+#endif
enum {
O_STRIP_OPTION = 0,
diff --git a/extensions/libxt_TCPOPTSTRIP.t b/extensions/libxt_TCPOPTSTRIP.t
new file mode 100644
index 00000000..b5c7a109
--- /dev/null
+++ b/extensions/libxt_TCPOPTSTRIP.t
@@ -0,0 +1,8 @@
+:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j TCPOPTSTRIP;;FAIL
+-p tcp -j TCPOPTSTRIP;=;OK
+-p tcp -j TCPOPTSTRIP --strip-options 2,3,4,5,6,7;=;OK
+-p tcp -j TCPOPTSTRIP --strip-options 0;;FAIL
+-p tcp -j TCPOPTSTRIP --strip-options 1;;FAIL
+-p tcp -j TCPOPTSTRIP --strip-options 1,2;;FAIL
diff --git a/extensions/libxt_TEE.c b/extensions/libxt_TEE.c
index 66c060d3..4676e33b 100644
--- a/extensions/libxt_TEE.c
+++ b/extensions/libxt_TEE.c
@@ -92,6 +92,40 @@ static void tee_tg6_save(const void *ip, const struct xt_entry_target *target)
printf(" --oif %s", info->oif);
}
+static int tee_tg_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_tee_tginfo *info = (const void *)params->target->data;
+
+ if (params->numeric)
+ xt_xlate_add(xl, "dup to %s",
+ xtables_ipaddr_to_numeric(&info->gw.in));
+ else
+ xt_xlate_add(xl, "dup to %s",
+ xtables_ipaddr_to_anyname(&info->gw.in));
+ if (*info->oif != '\0')
+ xt_xlate_add(xl, " device %s", info->oif);
+
+ return 1;
+}
+
+static int tee_tg6_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ const struct xt_tee_tginfo *info = (const void *)params->target->data;
+
+ if (params->numeric)
+ xt_xlate_add(xl, "dup to %s",
+ xtables_ip6addr_to_numeric(&info->gw.in6));
+ else
+ xt_xlate_add(xl, "dup to %s",
+ xtables_ip6addr_to_anyname(&info->gw.in6));
+ if (*info->oif != '\0')
+ xt_xlate_add(xl, " device %s", info->oif);
+
+ return 1;
+}
+
static struct xtables_target tee_tg_reg[] = {
{
.name = "TEE",
@@ -105,6 +139,7 @@ static struct xtables_target tee_tg_reg[] = {
.save = tee_tg_save,
.x6_parse = xtables_option_parse,
.x6_options = tee_tg_opts,
+ .xlate = tee_tg_xlate,
},
{
.name = "TEE",
@@ -118,6 +153,7 @@ static struct xtables_target tee_tg_reg[] = {
.save = tee_tg6_save,
.x6_parse = xtables_option_parse,
.x6_options = tee_tg_opts,
+ .xlate = tee_tg6_xlate,
},
};
diff --git a/extensions/libxt_TEE.t b/extensions/libxt_TEE.t
new file mode 100644
index 00000000..ce8b103e
--- /dev/null
+++ b/extensions/libxt_TEE.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD,OUTPUT
+-j TEE --gateway 1.1.1.1;=;OK
+-j TEE ! --gateway 1.1.1.1;;FAIL
+-j TEE;;FAIL
diff --git a/extensions/libxt_TOS.t b/extensions/libxt_TOS.t
new file mode 100644
index 00000000..ae8531cc
--- /dev/null
+++ b/extensions/libxt_TOS.t
@@ -0,0 +1,16 @@
+:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-j TOS --set-tos 0x1f;=;OK
+-j TOS --set-tos 0x1f/0x1f;=;OK
+# maximum TOS is 0x1f (5 bits)
+# ERROR: should fail: iptables -A PREROUTING -t mangle -j TOS --set-tos 0xff
+# -j TOS --set-tos 0xff;;FAIL
+-j TOS --set-tos Minimize-Delay;-j TOS --set-tos 0x10;OK
+-j TOS --set-tos Maximize-Throughput;-j TOS --set-tos 0x08;OK
+-j TOS --set-tos Maximize-Reliability;-j TOS --set-tos 0x04;OK
+-j TOS --set-tos Minimize-Cost;-j TOS --set-tos 0x02;OK
+-j TOS --set-tos Normal-Service;-j TOS --set-tos 0x00;OK
+-j TOS --and-tos 0x12;-j TOS --set-tos 0x00/0xed;OK
+-j TOS --or-tos 0x12;-j TOS --set-tos 0x12/0x12;OK
+-j TOS --xor-tos 0x12;-j TOS --set-tos 0x12/0x00;OK
+-j TOS;;FAIL
diff --git a/extensions/libxt_TPROXY.t b/extensions/libxt_TPROXY.t
new file mode 100644
index 00000000..12f82b1f
--- /dev/null
+++ b/extensions/libxt_TPROXY.t
@@ -0,0 +1,5 @@
+:PREROUTING
+*mangle
+-j TPROXY --on-port 12345 --on-ip 10.0.0.1 --tproxy-mark 0x23/0xff;;FAIL
+-p udp -j TPROXY --on-port 12345 --on-ip 10.0.0.1 --tproxy-mark 0x23/0xff;=;OK
+-p tcp -m tcp --dport 2342 -j TPROXY --on-port 12345 --on-ip 10.0.0.1 --tproxy-mark 0x23/0xff;=;OK
diff --git a/extensions/libxt_TRACE.c b/extensions/libxt_TRACE.c
index 0282e6ff..ac4f6fab 100644
--- a/extensions/libxt_TRACE.c
+++ b/extensions/libxt_TRACE.c
@@ -7,12 +7,20 @@
#include <xtables.h>
#include <linux/netfilter/x_tables.h>
+static int trace_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params)
+{
+ xt_xlate_add(xl, "nftrace set 1");
+ return 1;
+}
+
static struct xtables_target trace_target = {
.family = NFPROTO_UNSPEC,
.name = "TRACE",
.version = XTABLES_VERSION,
.size = XT_ALIGN(0),
.userspacesize = XT_ALIGN(0),
+ .xlate = trace_xlate,
};
void _init(void)
diff --git a/extensions/libxt_TRACE.t b/extensions/libxt_TRACE.t
new file mode 100644
index 00000000..cadb7330
--- /dev/null
+++ b/extensions/libxt_TRACE.t
@@ -0,0 +1,3 @@
+:PREROUTING,OUTPUT
+*raw
+-j TRACE;=;OK
diff --git a/extensions/libxt_addrtype.t b/extensions/libxt_addrtype.t
new file mode 100644
index 00000000..390a63f0
--- /dev/null
+++ b/extensions/libxt_addrtype.t
@@ -0,0 +1,17 @@
+:INPUT,FORWARD,OUTPUT
+-m addrtype;;FAIL
+-m addrtype --src-type wrong;;FAIL
+-m addrtype --src-type UNSPEC;=;OK
+-m addrtype --dst-type UNSPEC;=;OK
+-m addrtype --src-type LOCAL --dst-type LOCAL;=;OK
+-m addrtype --dst-type UNSPEC;=;OK
+-m addrtype --limit-iface-in;;FAIL
+-m addrtype --limit-iface-out;;FAIL
+-m addrtype --limit-iface-in --limit-iface-out;;FAIL
+-m addrtype --src-type LOCAL --limit-iface-in --limit-iface-out;;FAIL
+:INPUT
+-m addrtype --src-type LOCAL --limit-iface-in;=;OK
+-m addrtype --dst-type LOCAL --limit-iface-in;=;OK
+:OUTPUT
+-m addrtype --src-type LOCAL --limit-iface-out;=;OK
+-m addrtype --dst-type LOCAL --limit-iface-out;=;OK
diff --git a/extensions/libxt_bpf.c b/extensions/libxt_bpf.c
index dca97d70..92c445e8 100644
--- a/extensions/libxt_bpf.c
+++ b/extensions/libxt_bpf.c
@@ -16,11 +16,17 @@
#include <sys/types.h>
#include <unistd.h>
#include <xtables.h>
+#include "config.h"
+
+#ifdef HAVE_LINUX_BPF_H
+#include <linux/bpf.h>
+#endif
#define BCODE_FILE_MAX_LEN_B 1024
enum {
O_BCODE_STDIN = 0,
+ O_OBJ_PINNED = 1,
};
static void bpf_help(void)
@@ -28,7 +34,16 @@ static void bpf_help(void)
printf(
"bpf match options:\n"
"--bytecode <program> : a bpf program as generated by\n"
-" `nfbpf_compiler RAW <filter>`\n");
+" $(nfbpf_compile RAW '<filter>')\n");
+}
+
+static void bpf_help_v1(void)
+{
+ printf(
+"bpf match options:\n"
+"--bytecode <program> : a bpf program as generated by\n"
+" $(nfbpf_compile RAW '<filter>')\n"
+"--object-pinned <bpf object> : a path to a pinned BPF object in bpf fs\n");
}
static const struct xt_option_entry bpf_opts[] = {
@@ -36,23 +51,47 @@ static const struct xt_option_entry bpf_opts[] = {
XTOPT_TABLEEND,
};
-static void bpf_parse_string(struct xt_option_call *cb, const char *bpf_program,
- const char separator)
+static const struct xt_option_entry bpf_opts_v1[] = {
+ {.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING},
+ {.name = "object-pinned" , .id = O_OBJ_PINNED, .type = XTTYPE_STRING,
+ .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_bpf_info_v1, path)},
+ XTOPT_TABLEEND,
+};
+
+static int bpf_obj_get(const char *filepath)
{
- struct xt_bpf_info *bi = (void *) cb->data;
+#if defined HAVE_LINUX_BPF_H && defined __NR_bpf
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.pathname = (__u64) filepath;
+
+ return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
+#else
+ xtables_error(OTHER_PROBLEM,
+ "No bpf header, kernel headers too old?\n");
+ return -EINVAL;
+#endif
+}
+
+static void bpf_parse_string(struct sock_filter *pc, __u16 *lenp, __u16 len_max,
+ const char *bpf_program)
+{
+ const char separator = ',';
const char *token;
char sp;
int i;
+ __u16 len;
/* parse head: length. */
- if (sscanf(bpf_program, "%hu%c", &bi->bpf_program_num_elem, &sp) != 2 ||
+ if (sscanf(bpf_program, "%hu%c", &len, &sp) != 2 ||
sp != separator)
xtables_error(PARAMETER_PROBLEM,
"bpf: error parsing program length");
- if (!bi->bpf_program_num_elem)
+ if (!len)
xtables_error(PARAMETER_PROBLEM,
"bpf: illegal zero length program");
- if (bi->bpf_program_num_elem > XT_BPF_MAX_NUM_INSTR)
+ if (len > len_max)
xtables_error(PARAMETER_PROBLEM,
"bpf: number of instructions exceeds maximum");
@@ -60,62 +99,108 @@ static void bpf_parse_string(struct xt_option_call *cb, const char *bpf_program,
i = 0;
token = bpf_program;
while ((token = strchr(token, separator)) && (++token)[0]) {
- if (i >= bi->bpf_program_num_elem)
+ if (i >= len)
xtables_error(PARAMETER_PROBLEM,
"bpf: real program length exceeds"
" the encoded length parameter");
if (sscanf(token, "%hu %hhu %hhu %u,",
- &bi->bpf_program[i].code,
- &bi->bpf_program[i].jt,
- &bi->bpf_program[i].jf,
- &bi->bpf_program[i].k) != 4)
+ &pc->code, &pc->jt, &pc->jf, &pc->k) != 4)
xtables_error(PARAMETER_PROBLEM,
"bpf: error at instr %d", i);
i++;
+ pc++;
}
- if (i != bi->bpf_program_num_elem)
+ if (i != len)
xtables_error(PARAMETER_PROBLEM,
"bpf: parsed program length is less than the"
" encoded length parameter");
+
+ *lenp = len;
+}
+
+static void bpf_parse_obj_pinned(struct xt_bpf_info_v1 *bi,
+ const char *filepath)
+{
+ bi->fd = bpf_obj_get(filepath);
+ if (bi->fd < 0)
+ xtables_error(PARAMETER_PROBLEM,
+ "bpf: failed to get bpf object");
+
+ /* Cannot close bi->fd explicitly. Rely on exit */
+ if (fcntl(bi->fd, F_SETFD, FD_CLOEXEC) == -1) {
+ xtables_error(OTHER_PROBLEM,
+ "Could not set close on exec: %s\n",
+ strerror(errno));
+ }
}
static void bpf_parse(struct xt_option_call *cb)
{
+ struct xt_bpf_info *bi = (void *) cb->data;
+
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_BCODE_STDIN:
- bpf_parse_string(cb, cb->arg, ',');
+ bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
+ ARRAY_SIZE(bi->bpf_program), cb->arg);
break;
default:
xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
}
}
-static void bpf_print_code(const void *ip, const struct xt_entry_match *match)
+static void bpf_parse_v1(struct xt_option_call *cb)
{
- const struct xt_bpf_info *info = (void *) match->data;
- int i;
+ struct xt_bpf_info_v1 *bi = (void *) cb->data;
+
+ xtables_option_parse(cb);
+ switch (cb->entry->id) {
+ case O_BCODE_STDIN:
+ bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
+ ARRAY_SIZE(bi->bpf_program), cb->arg);
+ bi->mode = XT_BPF_MODE_BYTECODE;
+ break;
+ case O_OBJ_PINNED:
+ bpf_parse_obj_pinned(bi, cb->arg);
+ bi->mode = XT_BPF_MODE_FD_PINNED;
+ break;
+ default:
+ xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
+ }
+}
- for (i = 0; i < info->bpf_program_num_elem-1; i++)
- printf("%hu %hhu %hhu %u,", info->bpf_program[i].code,
- info->bpf_program[i].jt,
- info->bpf_program[i].jf,
- info->bpf_program[i].k);
+static void bpf_print_code(const struct sock_filter *pc, __u16 len, char tail)
+{
+ for (; len; len--, pc++)
+ printf("%hu %hhu %hhu %u%c",
+ pc->code, pc->jt, pc->jf, pc->k,
+ len > 1 ? ',' : tail);
+}
- printf("%hu %hhu %hhu %u", info->bpf_program[i].code,
- info->bpf_program[i].jt,
- info->bpf_program[i].jf,
- info->bpf_program[i].k);
+static void bpf_save_code(const struct sock_filter *pc, __u16 len)
+{
+ printf(" --bytecode \"%hu,", len);
+ bpf_print_code(pc, len, '\"');
}
static void bpf_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_bpf_info *info = (void *) match->data;
- printf(" --bytecode \"%hu,", info->bpf_program_num_elem);
- bpf_print_code(ip, match);
- printf("\"");
+ bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
+}
+
+static void bpf_save_v1(const void *ip, const struct xt_entry_match *match)
+{
+ const struct xt_bpf_info_v1 *info = (void *) match->data;
+
+ if (info->mode == XT_BPF_MODE_BYTECODE)
+ bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
+ else if (info->mode == XT_BPF_MODE_FD_PINNED)
+ printf(" --object-pinned %s", info->path);
+ else
+ xtables_error(OTHER_PROBLEM, "unknown bpf mode");
}
static void bpf_fcheck(struct xt_fcheck_call *cb)
@@ -125,28 +210,73 @@ static void bpf_fcheck(struct xt_fcheck_call *cb)
"bpf: missing --bytecode parameter");
}
+static void bpf_fcheck_v1(struct xt_fcheck_call *cb)
+{
+ const unsigned int bit_bcode = 1 << O_BCODE_STDIN;
+ const unsigned int bit_pinned = 1 << O_OBJ_PINNED;
+ unsigned int flags;
+
+ flags = cb->xflags & (bit_bcode | bit_pinned);
+ if (flags != bit_bcode && flags != bit_pinned)
+ xtables_error(PARAMETER_PROBLEM,
+ "bpf: one of --bytecode or --pinned is required");
+}
+
static void bpf_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
+ const struct xt_bpf_info *info = (void *) match->data;
+
+ printf("match bpf ");
+ bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
+}
+
+static void bpf_print_v1(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ const struct xt_bpf_info_v1 *info = (void *) match->data;
+
printf("match bpf ");
- return bpf_print_code(ip, match);
-}
-
-static struct xtables_match bpf_match = {
- .family = NFPROTO_UNSPEC,
- .name = "bpf",
- .version = XTABLES_VERSION,
- .size = XT_ALIGN(sizeof(struct xt_bpf_info)),
- .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
- .help = bpf_help,
- .print = bpf_print,
- .save = bpf_save,
- .x6_parse = bpf_parse,
- .x6_fcheck = bpf_fcheck,
- .x6_options = bpf_opts,
+ if (info->mode == XT_BPF_MODE_BYTECODE)
+ bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
+ else if (info->mode == XT_BPF_MODE_FD_PINNED)
+ printf("pinned %s", info->path);
+ else
+ printf("unknown");
+}
+
+static struct xtables_match bpf_matches[] = {
+ {
+ .family = NFPROTO_UNSPEC,
+ .name = "bpf",
+ .version = XTABLES_VERSION,
+ .revision = 0,
+ .size = XT_ALIGN(sizeof(struct xt_bpf_info)),
+ .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
+ .help = bpf_help,
+ .print = bpf_print,
+ .save = bpf_save,
+ .x6_parse = bpf_parse,
+ .x6_fcheck = bpf_fcheck,
+ .x6_options = bpf_opts,
+ },
+ {
+ .family = NFPROTO_UNSPEC,
+ .name = "bpf",
+ .version = XTABLES_VERSION,
+ .revision = 1,
+ .size = XT_ALIGN(sizeof(struct xt_bpf_info_v1)),
+ .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info_v1, filter)),
+ .help = bpf_help_v1,
+ .print = bpf_print_v1,
+ .save = bpf_save_v1,
+ .x6_parse = bpf_parse_v1,
+ .x6_fcheck = bpf_fcheck_v1,
+ .x6_options = bpf_opts_v1,
+ },
};
void _init(void)
{
- xtables_register_match(&bpf_match);
+ xtables_register_matches(bpf_matches, ARRAY_SIZE(bpf_matches));
}
diff --git a/extensions/libxt_bpf.man b/extensions/libxt_bpf.man
index 5b1d0424..1d2aa9e6 100644
--- a/extensions/libxt_bpf.man
+++ b/extensions/libxt_bpf.man
@@ -1,8 +1,21 @@
-Match using Linux Socket Filter. Expects a BPF program in decimal format. This
-is the format generated by the \fBnfbpf_compile\fP utility.
+Match using Linux Socket Filter. Expects a path to an eBPF object or a cBPF
+program in decimal format.
+.TP
+\fB\-\-object\-pinned\fP \fIpath\fP
+Pass a path to a pinned eBPF object.
+.PP
+Applications load eBPF programs into the kernel with the bpf() system call and
+BPF_PROG_LOAD command and can pin them in a virtual filesystem with BPF_OBJ_PIN.
+To use a pinned object in iptables, mount the bpf filesystem using
+.IP
+mount \-t bpf bpf ${BPF_MOUNT}
+.PP
+then insert the filter in iptables by path:
+.IP
+iptables \-A OUTPUT \-m bpf \-\-object\-pinned ${BPF_MOUNT}/{PINNED_PATH} \-j ACCEPT
.TP
\fB\-\-bytecode\fP \fIcode\fP
-Pass the BPF byte code format (described in the example below).
+Pass the BPF byte code format as generated by the \fBnfbpf_compile\fP utility.
.PP
The code format is similar to the output of the tcpdump -ddd command: one line
that stores the number of instructions, followed by one line for each
@@ -31,4 +44,17 @@ Or instead, you can invoke the nfbpf_compile utility.
.IP
iptables \-A OUTPUT \-m bpf \-\-bytecode "`nfbpf_compile RAW 'ip proto 6'`" \-j ACCEPT
.PP
+Or use tcpdump -ddd. In that case, generate BPF targeting a device with the
+same data link type as the xtables match. Iptables passes packets from the
+network layer up, without mac layer. Select a device with data link type RAW,
+such as a tun device:
+.IP
+ip tuntap add tun0 mode tun
+.br
+ip link set tun0 up
+.br
+tcpdump -ddd -i tun0 ip proto 6
+.PP
+See tcpdump -L -i $dev for a list of known data link types for a given device.
+.PP
You may want to learn more about BPF from FreeBSD's bpf(4) manpage.
diff --git a/extensions/libxt_bpf.t b/extensions/libxt_bpf.t
new file mode 100644
index 00000000..80361ad5
--- /dev/null
+++ b/extensions/libxt_bpf.t
@@ -0,0 +1,2 @@
+:INPUT,FORWARD,OUTPUT
+-m bpf --bytecode "4,48 0 0 9,21 0 1 6,6 0 0 1,6 0 0 0";=;OK
diff --git a/extensions/libxt_cgroup.c b/extensions/libxt_cgroup.c
new file mode 100644
index 00000000..480d64c9
--- /dev/null
+++ b/extensions/libxt_cgroup.c
@@ -0,0 +1,184 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_cgroup.h>
+
+enum {
+ O_CLASSID = 0,
+ O_PATH = 1,
+};
+
+static void cgroup_help_v0(void)
+{
+ printf(
+"cgroup match options:\n"
+"[!] --cgroup classid Match cgroup classid\n");
+}
+
+static void cgroup_help_v1(void)
+{
+ printf(
+"cgroup match options:\n"
+"[!] --path path Recursively match path relative to cgroup2 root\n"
+"[!] --cgroup classid Match cgroup classid, can't be used with --path\n");
+}
+
+static const struct xt_option_entry cgroup_opts_v0[] = {
+ {
+ .name = "cgroup",
+ .id = O_CLASSID,
+ .type = XTTYPE_UINT32,
+ .flags = XTOPT_INVERT | XTOPT_MAND | XTOPT_PUT,
+ XTOPT_POINTER(struct xt_cgroup_info_v0, id)
+ },
+ XTOPT_TABLEEND,
+};
+
+static const struct xt_option_entry cgroup_opts_v1[] = {
+ {
+ .name = "path",
+ .id = O_PATH,
+ .type = XTTYPE_STRING,
+ .flags = XTOPT_INVERT | XTOPT_PUT,
+ XTOPT_POINTER(struct xt_cgroup_info_v1, path)
+ },
+ {
+ .name = "cgroup",
+ .id = O_CLASSID,
+ .type = XTTYPE_UINT32,
+ .flags = XTOPT_INVERT | XTOPT_PUT,
+ XTOPT_POINTER(struct xt_cgroup_info_v1, classid)
+ },
+ XTOPT_TABLEEND,
+};
+
+static void cgroup_parse_v0(struct xt_option_call *cb)
+{
+ struct xt_cgroup_info_v0 *cgroupinfo = cb->data;
+
+ xtables_option_parse(cb);
+ if (cb->invert)
+ cgroupinfo->invert = true;
+}
+
+static void cgroup_parse_v1(struct xt_option_call *cb)
+{
+ struct xt_cgroup_info_v1 *info = cb->data;
+
+ xtables_option_parse(cb);
+
+ switch (cb->entry->id) {
+ case O_PATH:
+ info->has_path = true;
+ if (cb->invert)
+ info->invert_path = true;
+ break;
+ case O_CLASSID:
+ info->has_classid = true;
+ if (cb->invert)
+ info->invert_classid = true;
+ break;
+ }
+}
+
+static void
+cgroup_print_v0(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+ const struct xt_cgroup_info_v0 *info = (void *) match->data;
+
+ printf(" cgroup %s%u", info->invert ? "! ":"", info->id);
+}
+
+static void cgroup_save_v0(const void *ip, const struct xt_entry_match *match)
+{
+ const struct xt_cgroup_info_v0 *info = (void *) match->data;
+
+ printf("%s --cgroup %u", info->invert ? " !" : "", info->id);
+}
+
+static void
+cgroup_print_v1(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+ const struct xt_cgroup_info_v1 *info = (void *)match->data;
+
+ printf(" cgroup");
+ if (info->has_path)
+ printf(" %s%s", info->invert_path ? "! ":"", info->path);
+ if (info->has_classid)
+ printf(" %s%u", info->invert_classid ? "! ":"", info->classid);
+}
+
+static void cgroup_save_v1(const void *ip, const struct xt_entry_match *match)
+{
+ const struct xt_cgroup_info_v1 *info = (void *)match->data;
+
+ if (info->has_path) {
+ printf("%s --path", info->invert_path ? " !" : "");
+ xtables_save_string(info->path);
+ }
+
+ if (info->has_classid)
+ printf("%s --cgroup %u", info->invert_classid ? " !" : "",
+ info->classid);
+}
+
+static int cgroup_xlate_v0(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_cgroup_info_v0 *info = (void *)params->match->data;
+
+ xt_xlate_add(xl, "meta cgroup %s%u", info->invert ? "!= " : "",
+ info->id);
+ return 1;
+}
+
+static int cgroup_xlate_v1(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_cgroup_info_v1 *info = (void *)params->match->data;
+
+ if (info->has_path)
+ return 0;
+
+ if (info->has_classid)
+ xt_xlate_add(xl, "meta cgroup %s%u",
+ info->invert_classid ? "!= " : "",
+ info->classid);
+
+ return 1;
+}
+
+static struct xtables_match cgroup_match[] = {
+ {
+ .family = NFPROTO_UNSPEC,
+ .revision = 0,
+ .name = "cgroup",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_cgroup_info_v0)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_cgroup_info_v0)),
+ .help = cgroup_help_v0,
+ .print = cgroup_print_v0,
+ .save = cgroup_save_v0,
+ .x6_parse = cgroup_parse_v0,
+ .x6_options = cgroup_opts_v0,
+ .xlate = cgroup_xlate_v0,
+ },
+ {
+ .family = NFPROTO_UNSPEC,
+ .revision = 1,
+ .name = "cgroup",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_cgroup_info_v1)),
+ .userspacesize = offsetof(struct xt_cgroup_info_v1, priv),
+ .help = cgroup_help_v1,
+ .print = cgroup_print_v1,
+ .save = cgroup_save_v1,
+ .x6_parse = cgroup_parse_v1,
+ .x6_options = cgroup_opts_v1,
+ .xlate = cgroup_xlate_v1,
+ },
+};
+
+void _init(void)
+{
+ xtables_register_matches(cgroup_match, ARRAY_SIZE(cgroup_match));
+}
diff --git a/extensions/libxt_cgroup.man b/extensions/libxt_cgroup.man
new file mode 100644
index 00000000..4d5d1d86
--- /dev/null
+++ b/extensions/libxt_cgroup.man
@@ -0,0 +1,30 @@
+.TP
+[\fB!\fP] \fB\-\-path\fP \fIpath\fP
+Match cgroup2 membership.
+
+Each socket is associated with the v2 cgroup of the creating process.
+This matches packets coming from or going to all sockets in the
+sub-hierarchy of the specified path. The path should be relative to
+the root of the cgroup2 hierarchy.
+.TP
+[\fB!\fP] \fB\-\-cgroup\fP \fIclassid\fP
+Match cgroup net_cls classid.
+
+classid is the marker set through the cgroup net_cls controller. This
+option and \-\-path can't be used together.
+.PP
+Example:
+.IP
+iptables \-A OUTPUT \-p tcp \-\-sport 80 \-m cgroup ! \-\-path service/http-server \-j DROP
+.IP
+iptables \-A OUTPUT \-p tcp \-\-sport 80 \-m cgroup ! \-\-cgroup 1
+\-j DROP
+.PP
+\fBIMPORTANT\fP: when being used in the INPUT chain, the cgroup
+matcher is currently only of limited functionality, meaning it
+will only match on packets that are processed for local sockets
+through early socket demuxing. Therefore, general usage on the
+INPUT chain is not advised unless the implications are well
+understood.
+.PP
+Available since Linux 3.14.
diff --git a/extensions/libxt_cgroup.t b/extensions/libxt_cgroup.t
new file mode 100644
index 00000000..72c8e377
--- /dev/null
+++ b/extensions/libxt_cgroup.t
@@ -0,0 +1,8 @@
+:INPUT,OUTPUT,POSTROUTING
+*mangle
+-m cgroup --cgroup 1;=;OK
+-m cgroup ! --cgroup 1;=;OK
+-m cgroup --path "/";=;OK
+-m cgroup ! --path "/";=;OK
+-m cgroup --cgroup 1 --path "/";;FAIL
+-m cgroup ;;FAIL
diff --git a/extensions/libxt_cluster.man b/extensions/libxt_cluster.man
index 62ad71cc..94b4b205 100644
--- a/extensions/libxt_cluster.man
+++ b/extensions/libxt_cluster.man
@@ -55,6 +55,11 @@ arptables \-A INPUT \-i eth2 \-\-h\-length 6
\-\-destination\-mac 01:00:5e:00:01:02
\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27
.PP
+\fBNOTE\fP: the arptables commands above use mainstream syntax. If you
+are using arptables-jf included in some RedHat, CentOS and Fedora
+versions, you will hit syntax errors. Therefore, you'll have to adapt
+these to the arptables-jf syntax to get them working.
+.PP
In the case of TCP connections, pickup facility has to be disabled
to avoid marking TCP ACK packets coming in the reply direction as
valid.
diff --git a/extensions/libxt_cluster.t b/extensions/libxt_cluster.t
new file mode 100644
index 00000000..ac608244
--- /dev/null
+++ b/extensions/libxt_cluster.t
@@ -0,0 +1,10 @@
+:PREROUTING,FORWARD,POSTROUTING
+*mangle
+-m cluster;;FAIL
+-m cluster --cluster-total-nodes 3;;FAIL
+-m cluster --cluster-total-nodes 2 --cluster-local-node 2;;FAIL
+-m cluster --cluster-total-nodes 2 --cluster-local-node 3 --cluster-hash-seed;;FAIL
+#
+# outputs --cluster-local-nodemask instead of --cluster-local-node
+#
+-m cluster --cluster-total-nodes 2 --cluster-local-node 2 --cluster-hash-seed 0xfeedcafe;-m cluster --cluster-local-nodemask 0x00000002 --cluster-total-nodes 2 --cluster-hash-seed 0xfeedcafe;OK
diff --git a/extensions/libxt_comment.c b/extensions/libxt_comment.c
index 6ed2ff9b..b635d16c 100644
--- a/extensions/libxt_comment.c
+++ b/extensions/libxt_comment.c
@@ -48,6 +48,26 @@ comment_save(const void *ip, const struct xt_entry_match *match)
xtables_save_string(commentinfo->comment);
}
+static int comment_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ struct xt_comment_info *commentinfo = (void *)params->match->data;
+ char comment[XT_MAX_COMMENT_LEN];
+
+ commentinfo->comment[XT_MAX_COMMENT_LEN - 1] = '\0';
+ if (params->escape_quotes)
+ snprintf(comment, XT_MAX_COMMENT_LEN, "\\\"%s\\\"",
+ commentinfo->comment);
+ else
+ snprintf(comment, XT_MAX_COMMENT_LEN, "\"%s\"",
+ commentinfo->comment);
+
+ comment[XT_MAX_COMMENT_LEN - 1] = '\0';
+ xt_xlate_add_comment(xl, comment);
+
+ return 1;
+}
+
static struct xtables_match comment_match = {
.family = NFPROTO_UNSPEC,
.name = "comment",
@@ -59,6 +79,7 @@ static struct xtables_match comment_match = {
.save = comment_save,
.x6_parse = xtables_option_parse,
.x6_options = comment_opts,
+ .xlate = comment_xlate,
};
void _init(void)
diff --git a/extensions/libxt_comment.t b/extensions/libxt_comment.t
new file mode 100644
index 00000000..f12cd668
--- /dev/null
+++ b/extensions/libxt_comment.t
@@ -0,0 +1,12 @@
+:INPUT,FORWARD,OUTPUT
+-m comment;;FAIL
+-m comment --comment;;FAIL
+#
+# it fails with 256 characters
+#
+# should fail: iptables -A INPUT -m comment --comment xxxxxxxxxxxxxxxxx [....]
+# -m comment --comment xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;;FAIL
+#
+# success with 255 characters
+#
+-m comment --comment xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;=;OK
diff --git a/extensions/libxt_connbytes.t b/extensions/libxt_connbytes.t
new file mode 100644
index 00000000..6b24e266
--- /dev/null
+++ b/extensions/libxt_connbytes.t
@@ -0,0 +1,21 @@
+:INPUT,FORWARD,OUTPUT
+-m connbytes --connbytes 0:1000 --connbytes-mode packets --connbytes-dir original;=;OK
+-m connbytes --connbytes 0:1000 --connbytes-mode packets --connbytes-dir reply;=;OK
+-m connbytes --connbytes 0:1000 --connbytes-mode packets --connbytes-dir both;=;OK
+-m connbytes --connbytes 0:1000 --connbytes-mode bytes --connbytes-dir original;=;OK
+-m connbytes --connbytes 0:1000 --connbytes-mode bytes --connbytes-dir reply;=;OK
+-m connbytes --connbytes 0:1000 --connbytes-mode bytes --connbytes-dir both;=;OK
+-m connbytes --connbytes 0:1000 --connbytes-mode avgpkt --connbytes-dir original;=;OK
+-m connbytes --connbytes 0:1000 --connbytes-mode avgpkt --connbytes-dir reply;=;OK
+-m connbytes --connbytes 0:1000 --connbytes-mode avgpkt --connbytes-dir both;=;OK
+-m connbytes --connbytes -1:0 --connbytes-mode packets --connbytes-dir original;;FAIL
+-m connbytes --connbytes 0:-1 --connbytes-mode packets --connbytes-dir original;;FAIL
+# ERROR: cannot find: iptables -I INPUT -m connbytes --connbytes 0:18446744073709551615 --connbytes-mode avgpkt --connbytes-dir both
+# -m connbytes --connbytes 0:18446744073709551615 --connbytes-mode avgpkt --connbytes-dir both;=;OK
+-m connbytes --connbytes 0:18446744073709551616 --connbytes-mode avgpkt --connbytes-dir both;;FAIL
+-m connbytes --connbytes 0:1000 --connbytes-mode wrong --connbytes-dir both;;FAIL
+-m connbytes --connbytes 0:1000 --connbytes-dir original;;FAIL
+-m connbytes --connbytes 0:1000 --connbytes-mode packets;;FAIL
+-m connbytes --connbytes-dir original;;FAIL
+-m connbytes --connbytes 0:1000;;FAIL
+-m connbytes;;FAIL
diff --git a/extensions/libxt_connlabel.c b/extensions/libxt_connlabel.c
index c84a1671..d06bb27a 100644
--- a/extensions/libxt_connlabel.c
+++ b/extensions/libxt_connlabel.c
@@ -29,11 +29,36 @@ static const struct xt_option_entry connlabel_mt_opts[] = {
XTOPT_TABLEEND,
};
+/* cannot do this via _init, else static builds might spew error message
+ * for every iptables invocation.
+ */
+static void connlabel_open(void)
+{
+ const char *fname;
+
+ if (map)
+ return;
+
+ map = nfct_labelmap_new(NULL);
+ if (map != NULL)
+ return;
+
+ fname = nfct_labels_get_path();
+ if (errno) {
+ xtables_error(RESOURCE_PROBLEM,
+ "cannot open %s: %s", fname, strerror(errno));
+ } else {
+ xtables_error(RESOURCE_PROBLEM,
+ "cannot parse %s: no labels found", fname);
+ }
+}
+
static void connlabel_mt_parse(struct xt_option_call *cb)
{
struct xt_connlabel_mtinfo *info = cb->data;
int tmp;
+ connlabel_open();
xtables_option_parse(cb);
switch (cb->entry->id) {
@@ -54,7 +79,11 @@ static void connlabel_mt_parse(struct xt_option_call *cb)
static const char *connlabel_get_name(int b)
{
- const char *name = nfct_labelmap_get_name(map, b);
+ const char *name;
+
+ connlabel_open();
+
+ name = nfct_labelmap_get_name(map, b);
if (name && strcmp(name, ""))
return name;
return NULL;
@@ -99,6 +128,27 @@ connlabel_mt_save(const void *ip, const struct xt_entry_match *match)
connlabel_mt_print_op(info, "--");
}
+static int connlabel_mt_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_connlabel_mtinfo *info =
+ (const void *)params->match->data;
+ const char *name = connlabel_get_name(info->bit);
+
+ if (name == NULL)
+ return 0;
+
+ if (info->options & XT_CONNLABEL_OP_SET)
+ xt_xlate_add(xl, "ct label set %s ", name);
+
+ xt_xlate_add(xl, "ct label ");
+ if (info->options & XT_CONNLABEL_OP_INVERT)
+ xt_xlate_add(xl, "and %s != ", name);
+ xt_xlate_add(xl, "%s", name);
+
+ return 1;
+}
+
static struct xtables_match connlabel_mt_reg = {
.family = NFPROTO_UNSPEC,
.name = "connlabel",
@@ -110,15 +160,10 @@ static struct xtables_match connlabel_mt_reg = {
.save = connlabel_mt_save,
.x6_parse = connlabel_mt_parse,
.x6_options = connlabel_mt_opts,
+ .xlate = connlabel_mt_xlate,
};
void _init(void)
{
- map = nfct_labelmap_new(NULL);
- if (!map) {
- fprintf(stderr, "cannot open connlabel.conf, not registering '%s' match: %s\n",
- connlabel_mt_reg.name, strerror(errno));
- return;
- }
xtables_register_match(&connlabel_mt_reg);
}
diff --git a/extensions/libxt_connlabel.t b/extensions/libxt_connlabel.t
new file mode 100644
index 00000000..aad1032b
--- /dev/null
+++ b/extensions/libxt_connlabel.t
@@ -0,0 +1,18 @@
+:INPUT,FORWARD,OUTPUT
+# Backup the connlabel.conf, then add some label maps for test
+@[ -f /etc/xtables/connlabel.conf ] && mv /etc/xtables/connlabel.conf /tmp/connlabel.conf.bak
+@mkdir -p /etc/xtables
+@echo "40 bit40" > /etc/xtables/connlabel.conf
+@echo "41 bit41" >> /etc/xtables/connlabel.conf
+@echo "128 bit128" >> /etc/xtables/connlabel.conf
+-m connlabel --label "bit40";=;OK
+-m connlabel ! --label "bit40";=;OK
+-m connlabel --label "bit41" --set;=;OK
+-m connlabel ! --label "bit41" --set;=;OK
+-m connlabel --label "bit128";;FAIL
+@echo > /etc/xtables/connlabel.conf
+-m connlabel --label "abc";;FAIL
+@rm -f /etc/xtables/connlabel.conf
+-m connlabel --label "abc";;FAIL
+# Restore the original connlabel.conf
+@[ -f /tmp/connlabel.conf.bak ] && mv /tmp/connlabel.conf.bak /etc/xtables/connlabel.conf
diff --git a/extensions/libxt_connlimit.t b/extensions/libxt_connlimit.t
new file mode 100644
index 00000000..c7ea61e9
--- /dev/null
+++ b/extensions/libxt_connlimit.t
@@ -0,0 +1,16 @@
+:INPUT,FORWARD,OUTPUT
+-m connlimit --connlimit-upto 0;=;OK
+-m connlimit --connlimit-upto 4294967295;=;OK
+-m connlimit --connlimit-upto 4294967296;;FAIL
+-m connlimit --connlimit-upto -1;;FAIL
+-m connlimit --connlimit-above 0;=;OK
+-m connlimit --connlimit-above 4294967295;=;OK
+-m connlimit --connlimit-above 4294967296;;FAIL
+-m connlimit --connlimit-above -1;;FAIL
+-m connlimit --connlimit-upto 1 --conlimit-above 1;;FAIL
+-m connlimit --connlimit-above 10 --connlimit-saddr;-m connlimit --connlimit-above 10 --connlimit-mask 32 --connlimit-saddr;OK
+-m connlimit --connlimit-above 10 --connlimit-daddr;-m connlimit --connlimit-above 10 --connlimit-mask 32 --connlimit-daddr;OK
+-m connlimit --connlimit-above 10 --connlimit-saddr --connlimit-daddr;;FAIL
+-m connlimit --connlimit-above 10 --connlimit-mask 32 --connlimit-saddr;=;OK
+-m connlimit --connlimit-above 10 --connlimit-mask 32 --connlimit-daddr;=;OK
+-m connlimit;;FAIL
diff --git a/extensions/libxt_connmark.c b/extensions/libxt_connmark.c
index 6f1d5323..be3499b6 100644
--- a/extensions/libxt_connmark.c
+++ b/extensions/libxt_connmark.c
@@ -17,7 +17,7 @@
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdbool.h>
#include <stdint.h>
@@ -89,7 +89,8 @@ connmark_print(const void *ip, const struct xt_entry_match *match, int numeric)
}
static void
-connmark_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
+connmark_mt_print(const void *ip, const struct xt_entry_match *match,
+ int numeric)
{
const struct xt_connmark_mtinfo1 *info = (const void *)match->data;
@@ -122,6 +123,49 @@ connmark_mt_save(const void *ip, const struct xt_entry_match *match)
print_mark(info->mark, info->mask);
}
+static void print_mark_xlate(unsigned int mark, unsigned int mask,
+ struct xt_xlate *xl, uint32_t op)
+{
+ if (mask != 0xffffffffU)
+ xt_xlate_add(xl, " and 0x%x %s 0x%x", mask,
+ op == XT_OP_EQ ? "==" : "!=", mark);
+ else
+ xt_xlate_add(xl, " %s0x%x",
+ op == XT_OP_EQ ? "" : "!= ", mark);
+}
+
+static int connmark_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_connmark_info *info = (const void *)params->match->data;
+ enum xt_op op = XT_OP_EQ;
+
+ if (info->invert)
+ op = XT_OP_NEQ;
+
+ xt_xlate_add(xl, "ct mark");
+ print_mark_xlate(info->mark, info->mask, xl, op);
+
+ return 1;
+}
+
+static int
+connmark_mt_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_connmark_mtinfo1 *info =
+ (const void *)params->match->data;
+ enum xt_op op = XT_OP_EQ;
+
+ if (info->invert)
+ op = XT_OP_NEQ;
+
+ xt_xlate_add(xl, "ct mark");
+ print_mark_xlate(info->mark, info->mask, xl, op);
+
+ return 1;
+}
+
static struct xtables_match connmark_mt_reg[] = {
{
.family = NFPROTO_UNSPEC,
@@ -135,6 +179,7 @@ static struct xtables_match connmark_mt_reg[] = {
.save = connmark_save,
.x6_parse = connmark_parse,
.x6_options = connmark_mt_opts,
+ .xlate = connmark_xlate,
},
{
.version = XTABLES_VERSION,
@@ -148,6 +193,7 @@ static struct xtables_match connmark_mt_reg[] = {
.save = connmark_mt_save,
.x6_parse = connmark_mt_parse,
.x6_options = connmark_mt_opts,
+ .xlate = connmark_mt_xlate,
},
};
diff --git a/extensions/libxt_connmark.t b/extensions/libxt_connmark.t
new file mode 100644
index 00000000..4dd7d9af
--- /dev/null
+++ b/extensions/libxt_connmark.t
@@ -0,0 +1,9 @@
+:PREROUTING,FORWARD,OUTPUT,POSTROUTING
+*mangle
+-m connmark --mark 0xffffffff;=;OK
+-m connmark --mark 0xffffffff/0xffffffff;-m connmark --mark 0xffffffff;OK
+-m connmark --mark 0xffffffff/0;=;OK
+-m connmark --mark 0/0xffffffff;-m connmark --mark 0;OK
+-m connmark --mark -1;;FAIL
+-m connmark --mark 0xfffffffff;;FAIL
+-m connmark;;FAIL
diff --git a/extensions/libxt_conntrack.c b/extensions/libxt_conntrack.c
index 128bbd20..72c52200 100644
--- a/extensions/libxt_conntrack.c
+++ b/extensions/libxt_conntrack.c
@@ -1156,6 +1156,246 @@ static void state_save(const void *ip, const struct xt_entry_match *match)
state_print_state(sinfo->statemask);
}
+static void state_xlate_print(struct xt_xlate *xl, unsigned int statemask)
+{
+ const char *sep = "";
+
+ if (statemask & XT_CONNTRACK_STATE_INVALID) {
+ xt_xlate_add(xl, "%s%s", sep, "invalid");
+ sep = ",";
+ }
+ if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
+ xt_xlate_add(xl, "%s%s", sep, "new");
+ sep = ",";
+ }
+ if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
+ xt_xlate_add(xl, "%s%s", sep, "related");
+ sep = ",";
+ }
+ if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
+ xt_xlate_add(xl, "%s%s", sep, "established");
+ sep = ",";
+ }
+ if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
+ xt_xlate_add(xl, "%s%s", sep, "untracked");
+ sep = ",";
+ }
+}
+
+static int state_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_conntrack_mtinfo3 *sinfo =
+ (const void *)params->match->data;
+
+ xt_xlate_add(xl, "ct state %s", sinfo->invert_flags & XT_CONNTRACK_STATE ?
+ "!= " : "");
+ state_xlate_print(xl, sinfo->state_mask);
+ xt_xlate_add(xl, " ");
+ return 1;
+}
+
+static void status_xlate_print(struct xt_xlate *xl, unsigned int statusmask)
+{
+ const char *sep = "";
+
+ if (statusmask & IPS_EXPECTED) {
+ xt_xlate_add(xl, "%s%s", sep, "expected");
+ sep = ",";
+ }
+ if (statusmask & IPS_SEEN_REPLY) {
+ xt_xlate_add(xl, "%s%s", sep, "seen-reply");
+ sep = ",";
+ }
+ if (statusmask & IPS_ASSURED) {
+ xt_xlate_add(xl, "%s%s", sep, "assured");
+ sep = ",";
+ }
+ if (statusmask & IPS_CONFIRMED) {
+ xt_xlate_add(xl, "%s%s", sep, "confirmed");
+ sep = ",";
+ }
+}
+
+static void addr_xlate_print(struct xt_xlate *xl,
+ const union nf_inet_addr *addr,
+ const union nf_inet_addr *mask,
+ unsigned int family)
+{
+ if (family == NFPROTO_IPV4) {
+ xt_xlate_add(xl, "%s%s", xtables_ipaddr_to_numeric(&addr->in),
+ xtables_ipmask_to_numeric(&mask->in));
+ } else if (family == NFPROTO_IPV6) {
+ xt_xlate_add(xl, "%s%s", xtables_ip6addr_to_numeric(&addr->in6),
+ xtables_ip6mask_to_numeric(&mask->in6));
+ }
+}
+
+static int _conntrack3_mt_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params,
+ int family)
+{
+ const struct xt_conntrack_mtinfo3 *sinfo =
+ (const void *)params->match->data;
+ char *space = "";
+
+ if (sinfo->match_flags & XT_CONNTRACK_DIRECTION) {
+ xt_xlate_add(xl, "ct direction %s",
+ sinfo->invert_flags & XT_CONNTRACK_DIRECTION ?
+ "reply" : "original");
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_PROTO) {
+ xt_xlate_add(xl, "%sct %s protocol %s%u", space,
+ sinfo->invert_flags & XT_CONNTRACK_DIRECTION ?
+ "reply" : "original",
+ sinfo->invert_flags & XT_CONNTRACK_PROTO ?
+ "!= " : "",
+ sinfo->l4proto);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_STATE) {
+ xt_xlate_add(xl, "%sct state %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_STATE ?
+ "!= " : "");
+ state_xlate_print(xl, sinfo->state_mask);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_STATUS) {
+ if (sinfo->status_mask == 1)
+ return 0;
+ xt_xlate_add(xl, "%sct status %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_STATUS ?
+ "!= " : "");
+ status_xlate_print(xl, sinfo->status_mask);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_EXPIRES) {
+ xt_xlate_add(xl, "%sct expiration %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_EXPIRES ?
+ "!= " : "");
+ if (sinfo->expires_max == sinfo->expires_min)
+ xt_xlate_add(xl, "%lu", sinfo->expires_min);
+ else
+ xt_xlate_add(xl, "%lu-%lu", sinfo->expires_min,
+ sinfo->expires_max);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_ORIGSRC) {
+ if (&sinfo->origsrc_addr == 0L)
+ return 0;
+
+ xt_xlate_add(xl, "%sct original saddr %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_ORIGSRC ?
+ "!= " : "");
+ addr_xlate_print(xl, &sinfo->origsrc_addr,
+ &sinfo->origsrc_mask, family);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_ORIGDST) {
+ if (&sinfo->origdst_addr == 0L)
+ return 0;
+
+ xt_xlate_add(xl, "%sct original daddr %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_ORIGDST ?
+ "!= " : "");
+ addr_xlate_print(xl, &sinfo->origdst_addr,
+ &sinfo->origdst_mask, family);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_REPLSRC) {
+ if (&sinfo->replsrc_addr == 0L)
+ return 0;
+
+ xt_xlate_add(xl, "%sct reply saddr %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_REPLSRC ?
+ "!= " : "");
+ addr_xlate_print(xl, &sinfo->replsrc_addr,
+ &sinfo->replsrc_mask, family);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_REPLDST) {
+ if (&sinfo->repldst_addr == 0L)
+ return 0;
+
+ xt_xlate_add(xl, "%sct reply daddr %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_REPLDST ?
+ "!= " : "");
+ addr_xlate_print(xl, &sinfo->repldst_addr,
+ &sinfo->repldst_mask, family);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
+ xt_xlate_add(xl, "%sct original proto-src %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_ORIGSRC_PORT ?
+ "!= " : "");
+ if (sinfo->origsrc_port == sinfo->origsrc_port_high)
+ xt_xlate_add(xl, "%u", sinfo->origsrc_port);
+ else
+ xt_xlate_add(xl, "%u-%u", sinfo->origsrc_port,
+ sinfo->origsrc_port_high);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
+ xt_xlate_add(xl, "%sct original proto-dst %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_ORIGDST_PORT ?
+ "!= " : "");
+ if (sinfo->origdst_port == sinfo->origdst_port_high)
+ xt_xlate_add(xl, "%u", sinfo->origdst_port);
+ else
+ xt_xlate_add(xl, "%u-%u", sinfo->origdst_port,
+ sinfo->origdst_port_high);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
+ xt_xlate_add(xl, "%sct reply proto-src %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_REPLSRC_PORT ?
+ "!= " : "");
+ if (sinfo->replsrc_port == sinfo->replsrc_port_high)
+ xt_xlate_add(xl, "%u", sinfo->replsrc_port);
+ else
+ xt_xlate_add(xl, "%u-%u", sinfo->replsrc_port,
+ sinfo->replsrc_port_high);
+ space = " ";
+ }
+
+ if (sinfo->match_flags & XT_CONNTRACK_REPLDST_PORT) {
+ xt_xlate_add(xl, "%sct reply proto-dst %s", space,
+ sinfo->invert_flags & XT_CONNTRACK_REPLDST_PORT ?
+ "!= " : "", sinfo->repldst_port);
+ if (sinfo->repldst_port == sinfo->repldst_port_high)
+ xt_xlate_add(xl, "%u", sinfo->repldst_port);
+ else
+ xt_xlate_add(xl, "%u-%u", sinfo->repldst_port,
+ sinfo->repldst_port_high);
+ }
+
+ return 1;
+}
+
+static int conntrack3_mt4_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ return _conntrack3_mt_xlate(xl, params, NFPROTO_IPV4);
+}
+
+static int conntrack3_mt6_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ return _conntrack3_mt_xlate(xl, params, NFPROTO_IPV6);
+}
+
static struct xtables_match conntrack_mt_reg[] = {
{
.version = XTABLES_VERSION,
@@ -1246,6 +1486,7 @@ static struct xtables_match conntrack_mt_reg[] = {
.save = conntrack3_mt_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack3_mt_opts,
+ .xlate = conntrack3_mt4_xlate,
},
{
.version = XTABLES_VERSION,
@@ -1261,6 +1502,7 @@ static struct xtables_match conntrack_mt_reg[] = {
.save = conntrack3_mt6_save,
.alias = conntrack_print_name_alias,
.x6_options = conntrack3_mt_opts,
+ .xlate = conntrack3_mt6_xlate,
},
{
.family = NFPROTO_UNSPEC,
@@ -1306,6 +1548,7 @@ static struct xtables_match conntrack_mt_reg[] = {
.save = state_save,
.x6_parse = state_ct23_parse,
.x6_options = state_opts,
+ .xlate = state_xlate,
},
{
.family = NFPROTO_UNSPEC,
diff --git a/extensions/libxt_conntrack.man b/extensions/libxt_conntrack.man
index 15fd1ddf..4b13f0f6 100644
--- a/extensions/libxt_conntrack.man
+++ b/extensions/libxt_conntrack.man
@@ -45,7 +45,7 @@ States for \fB\-\-ctstate\fP:
The packet is associated with no known connection.
.TP
\fBNEW\fP
-The packet has started a new connection, or otherwise associated
+The packet has started a new connection or otherwise associated
with a connection which has not seen packets in both directions.
.TP
\fBESTABLISHED\fP
@@ -54,7 +54,7 @@ in both directions.
.TP
\fBRELATED\fP
The packet is starting a new connection, but is associated with an
-existing connection, such as an FTP data transfer, or an ICMP error.
+existing connection, such as an FTP data transfer or an ICMP error.
.TP
\fBUNTRACKED\fP
The packet is not tracked at all, which happens if you explicitly untrack it
diff --git a/extensions/libxt_conntrack.t b/extensions/libxt_conntrack.t
new file mode 100644
index 00000000..db531475
--- /dev/null
+++ b/extensions/libxt_conntrack.t
@@ -0,0 +1,27 @@
+:INPUT,FORWARD,OUTPUT
+-m conntrack --ctstate NEW;=;OK
+-m conntrack --ctstate NEW,ESTABLISHED;=;OK
+-m conntrack --ctstate NEW,RELATED,ESTABLISHED;=;OK
+-m conntrack --ctstate INVALID;=;OK
+-m conntrack --ctstate UNTRACKED;=;OK
+-m conntrack --ctstate SNAT,DNAT;=;OK
+-m conntrack --ctstate wrong;;FAIL
+# should we convert this to output "tcp" instead of 6?
+-m conntrack --ctproto tcp;-m conntrack --ctproto 6;OK
+-m conntrack --ctorigsrc 1.1.1.1;=;OK
+-m conntrack --ctorigdst 1.1.1.1;=;OK
+-m conntrack --ctreplsrc 1.1.1.1;=;OK
+-m conntrack --ctrepldst 1.1.1.1;=;OK
+-m conntrack --ctexpire 0;=;OK
+-m conntrack --ctexpire 4294967295;=;OK
+-m conntrack --ctexpire 0:4294967295;=;OK
+-m conntrack --ctexpire 42949672956;;FAIL
+-m conntrack --ctexpire -1;;FAIL
+-m conntrack --ctdir ORIGINAL;=;OK
+-m conntrack --ctdir REPLY;=;OK
+-m conntrack --ctstatus NONE;=;OK
+-m conntrack --ctstatus CONFIRMED;=;OK
+-m conntrack --ctstatus ASSURED;=;OK
+-m conntrack --ctstatus EXPECTED;=;OK
+-m conntrack --ctstatus SEEN_REPLY;=;OK
+-m conntrack;;FAIL
diff --git a/extensions/libxt_cpu.c b/extensions/libxt_cpu.c
index 404a6a66..41c13c3c 100644
--- a/extensions/libxt_cpu.c
+++ b/extensions/libxt_cpu.c
@@ -44,9 +44,19 @@ static void cpu_save(const void *ip, const struct xt_entry_match *match)
printf("%s --cpu %u", info->invert ? " !" : "", info->cpu);
}
+static int cpu_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_cpu_info *info = (void *)params->match->data;
+
+ xt_xlate_add(xl, "cpu%s %u", info->invert ? " !=" : "", info->cpu);
+
+ return 1;
+}
+
static struct xtables_match cpu_match = {
.family = NFPROTO_UNSPEC,
- .name = "cpu",
+ .name = "cpu",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_cpu_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_cpu_info)),
@@ -55,6 +65,7 @@ static struct xtables_match cpu_match = {
.save = cpu_save,
.x6_parse = cpu_parse,
.x6_options = cpu_opts,
+ .xlate = cpu_xlate,
};
void _init(void)
diff --git a/extensions/libxt_cpu.t b/extensions/libxt_cpu.t
new file mode 100644
index 00000000..f5adb45d
--- /dev/null
+++ b/extensions/libxt_cpu.t
@@ -0,0 +1,6 @@
+:INPUT,FORWARD,OUTPUT
+-m cpu --cpu 0;=;OK
+-m cpu ! --cpu 0;=;OK
+-m cpu --cpu 4294967295;=;OK
+-m cpu --cpu 4294967296;;FAIL
+-m cpu;;FAIL
diff --git a/extensions/libxt_dccp.c b/extensions/libxt_dccp.c
index a35cabbd..5e67c264 100644
--- a/extensions/libxt_dccp.c
+++ b/extensions/libxt_dccp.c
@@ -277,6 +277,97 @@ static void dccp_save(const void *ip, const struct xt_entry_match *match)
}
}
+static const char *const dccp_pkt_types_xlate[] = {
+ [DCCP_PKT_REQUEST] = "request",
+ [DCCP_PKT_RESPONSE] = "response",
+ [DCCP_PKT_DATA] = "data",
+ [DCCP_PKT_ACK] = "ack",
+ [DCCP_PKT_DATAACK] = "dataack",
+ [DCCP_PKT_CLOSEREQ] = "closereq",
+ [DCCP_PKT_CLOSE] = "close",
+ [DCCP_PKT_RESET] = "reset",
+ [DCCP_PKT_SYNC] = "sync",
+ [DCCP_PKT_SYNCACK] = "syncack",
+};
+
+static int dccp_type_xlate(const struct xt_dccp_info *einfo,
+ struct xt_xlate *xl)
+{
+ bool have_type = false, set_need = false;
+ uint16_t types = einfo->typemask;
+
+ if (types & (1 << DCCP_PKT_INVALID))
+ return 0;
+
+ xt_xlate_add(xl, " dccp type%s ", einfo->invflags ? " !=" : "");
+
+ if ((types != 0) && !(types == (types & -types))) {
+ xt_xlate_add(xl, "{");
+ set_need = true;
+ }
+
+ while (types) {
+ unsigned int i;
+
+ for (i = 0; !(types & (1 << i)); i++);
+
+ if (have_type)
+ xt_xlate_add(xl, ", ");
+ else
+ have_type = true;
+
+ xt_xlate_add(xl, "%s", dccp_pkt_types_xlate[i]);
+
+ types &= ~(1 << i);
+ }
+
+ if (set_need)
+ xt_xlate_add(xl, "}");
+
+ return 1;
+}
+
+static int dccp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_dccp_info *einfo =
+ (const struct xt_dccp_info *)params->match->data;
+ char *space = "";
+ int ret = 1;
+
+ xt_xlate_add(xl, "dccp ");
+
+ if (einfo->flags & XT_DCCP_SRC_PORTS) {
+ if (einfo->spts[0] != einfo->spts[1])
+ xt_xlate_add(xl, "sport%s %u-%u",
+ einfo->invflags & XT_DCCP_SRC_PORTS ? " !=" : "",
+ einfo->spts[0], einfo->spts[1]);
+ else
+ xt_xlate_add(xl, "sport%s %u",
+ einfo->invflags & XT_DCCP_SRC_PORTS ? " !=" : "",
+ einfo->spts[0]);
+ space = " ";
+ }
+
+ if (einfo->flags & XT_DCCP_DEST_PORTS) {
+ if (einfo->dpts[0] != einfo->dpts[1])
+ xt_xlate_add(xl, "%sdport%s %u-%u", space,
+ einfo->invflags & XT_DCCP_DEST_PORTS ? " !=" : "",
+ einfo->dpts[0], einfo->dpts[1]);
+ else
+ xt_xlate_add(xl, "%sdport%s %u", space,
+ einfo->invflags & XT_DCCP_DEST_PORTS ? " !=" : "",
+ einfo->dpts[0]);
+ }
+
+ if (einfo->flags & XT_DCCP_TYPE)
+ ret = dccp_type_xlate(einfo, xl);
+
+ if (einfo->flags & XT_DCCP_OPTION)
+ ret = 0;
+
+ return ret;
+}
static struct xtables_match dccp_match = {
.name = "dccp",
.family = NFPROTO_UNSPEC,
@@ -288,6 +379,7 @@ static struct xtables_match dccp_match = {
.save = dccp_save,
.x6_parse = dccp_parse,
.x6_options = dccp_opts,
+ .xlate = dccp_xlate,
};
void _init(void)
diff --git a/extensions/libxt_dccp.t b/extensions/libxt_dccp.t
new file mode 100644
index 00000000..f60b480f
--- /dev/null
+++ b/extensions/libxt_dccp.t
@@ -0,0 +1,30 @@
+:INPUT,FORWARD,OUTPUT
+-p dccp -m dccp --sport 1;=;OK
+-p dccp -m dccp --sport 65535;=;OK
+-p dccp -m dccp --dport 1;=;OK
+-p dccp -m dccp --dport 65535;=;OK
+-p dccp -m dccp --sport 1:1023;=;OK
+-p dccp -m dccp --sport 1024:65535;=;OK
+-p dccp -m dccp --sport 1024:;-p dccp -m dccp --sport 1024:65535;OK
+-p dccp -m dccp ! --sport 1;=;OK
+-p dccp -m dccp ! --sport 65535;=;OK
+-p dccp -m dccp ! --dport 1;=;OK
+-p dccp -m dccp ! --dport 65535;=;OK
+-p dccp -m dccp --sport 1 --dport 65535;=;OK
+-p dccp -m dccp --sport 65535 --dport 1;=;OK
+-p dccp -m dccp ! --sport 1 --dport 65535;=;OK
+-p dccp -m dccp ! --sport 65535 --dport 1;=;OK
+# ERROR: should fail: iptables -A INPUT -p dccp -m dccp --sport 65536
+# -p dccp -m dccp --sport 65536;;FAIL
+-p dccp -m dccp --sport -1;;FAIL
+-p dccp -m dccp --dport -1;;FAIL
+-p dccp -m dccp --dccp-types REQUEST,RESPONSE,DATA,ACK,DATAACK,CLOSEREQ,CLOSE,RESET,SYNC,SYNCACK,INVALID;=;OK
+-p dccp -m dccp ! --dccp-types REQUEST,RESPONSE,DATA,ACK,DATAACK,CLOSEREQ,CLOSE,RESET,SYNC,SYNCACK,INVALID;=;OK
+# DCCP option 0 is valid, see http://tools.ietf.org/html/rfc4340#page-29
+# ERROR: cannot load: iptables -A INPUT -p dccp -m dccp --dccp-option 0
+#-p dccp -m dccp --dccp-option 0;=;OK
+-p dccp -m dccp --dccp-option 255;=;OK
+-p dccp -m dccp --dccp-option 256;;FAIL
+-p dccp -m dccp --dccp-option -1;;FAIL
+# should we accept this below?
+-p dccp -m dccp;=;OK
diff --git a/extensions/libxt_devgroup.c b/extensions/libxt_devgroup.c
index 4a69c822..2ec3905c 100644
--- a/extensions/libxt_devgroup.c
+++ b/extensions/libxt_devgroup.c
@@ -31,12 +31,12 @@ static const struct xt_option_entry devgroup_opts[] = {
XTOPT_TABLEEND,
};
-/* array of devgroups from /etc/iproute2/group_map */
+/* array of devgroups from /etc/iproute2/group */
static struct xtables_lmap *devgroups;
static void devgroup_init(struct xt_entry_match *match)
{
- const char file[] = "/etc/iproute2/group_map";
+ const char file[] = "/etc/iproute2/group";
devgroups = xtables_lmap_init(file);
if (devgroups == NULL && errno != ENOENT)
fprintf(stderr, "Warning: %s: %s\n", file, strerror(errno));
@@ -124,7 +124,7 @@ static void devgroup_show(const char *pfx, const struct xt_devgroup_info *info,
if (info->flags & XT_DEVGROUP_INVERT_DST)
printf(" !");
printf(" %sdst-group ", pfx);
- print_devgroup(info->src_group, info->src_mask, numeric);
+ print_devgroup(info->dst_group, info->dst_mask, numeric);
}
}
@@ -151,6 +151,61 @@ static void devgroup_check(struct xt_fcheck_call *cb)
"'--src-group' or '--dst-group'");
}
+static void
+print_devgroup_xlate(unsigned int id, uint32_t op, unsigned int mask,
+ struct xt_xlate *xl, int numeric)
+{
+ const char *name = NULL;
+
+ if (mask != 0xffffffff)
+ xt_xlate_add(xl, "and 0x%x %s 0x%x", mask,
+ op == XT_OP_EQ ? "==" : "!=", id);
+ else {
+ if (numeric == 0)
+ name = xtables_lmap_id2name(devgroups, id);
+
+ xt_xlate_add(xl, "%s", op == XT_OP_EQ ? "" : "!= ");
+ if (name)
+ xt_xlate_add(xl, "%s", name);
+ else
+ xt_xlate_add(xl, "0x%x", id);
+ }
+}
+
+static void devgroup_show_xlate(const struct xt_devgroup_info *info,
+ struct xt_xlate *xl, int numeric)
+{
+ enum xt_op op = XT_OP_EQ;
+ char *space = "";
+
+ if (info->flags & XT_DEVGROUP_MATCH_SRC) {
+ if (info->flags & XT_DEVGROUP_INVERT_SRC)
+ op = XT_OP_NEQ;
+ xt_xlate_add(xl, "iifgroup ");
+ print_devgroup_xlate(info->src_group, op,
+ info->src_mask, xl, numeric);
+ space = " ";
+ }
+
+ if (info->flags & XT_DEVGROUP_MATCH_DST) {
+ if (info->flags & XT_DEVGROUP_INVERT_DST)
+ op = XT_OP_NEQ;
+ xt_xlate_add(xl, "%soifgroup ", space);
+ print_devgroup_xlate(info->dst_group, op,
+ info->dst_mask, xl, numeric);
+ }
+}
+
+static int devgroup_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_devgroup_info *info = (const void *)params->match->data;
+
+ devgroup_show_xlate(info, xl, 0);
+
+ return 1;
+}
+
static struct xtables_match devgroup_mt_reg = {
.name = "devgroup",
.version = XTABLES_VERSION,
@@ -164,6 +219,7 @@ static struct xtables_match devgroup_mt_reg = {
.x6_parse = devgroup_parse,
.x6_fcheck = devgroup_check,
.x6_options = devgroup_opts,
+ .xlate = devgroup_xlate,
};
void _init(void)
diff --git a/extensions/libxt_dscp.c b/extensions/libxt_dscp.c
index 02b22a4e..d5c73236 100644
--- a/extensions/libxt_dscp.c
+++ b/extensions/libxt_dscp.c
@@ -91,21 +91,66 @@ static void dscp_save(const void *ip, const struct xt_entry_match *match)
printf("%s --dscp 0x%02x", dinfo->invert ? " !" : "", dinfo->dscp);
}
-static struct xtables_match dscp_match = {
- .family = NFPROTO_UNSPEC,
- .name = "dscp",
- .version = XTABLES_VERSION,
- .size = XT_ALIGN(sizeof(struct xt_dscp_info)),
- .userspacesize = XT_ALIGN(sizeof(struct xt_dscp_info)),
- .help = dscp_help,
- .print = dscp_print,
- .save = dscp_save,
- .x6_parse = dscp_parse,
- .x6_fcheck = dscp_check,
- .x6_options = dscp_opts,
+static int __dscp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_dscp_info *dinfo =
+ (const struct xt_dscp_info *)params->match->data;
+
+ xt_xlate_add(xl, "dscp %s0x%02x", dinfo->invert ? "!= " : "",
+ dinfo->dscp);
+
+ return 1;
+}
+
+static int dscp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ xt_xlate_add(xl, "ip ");
+
+ return __dscp_xlate(xl, params);
+}
+
+static int dscp_xlate6(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ xt_xlate_add(xl, "ip6 ");
+
+ return __dscp_xlate(xl, params);
+}
+
+static struct xtables_match dscp_mt_reg[] = {
+ {
+ .family = NFPROTO_IPV4,
+ .name = "dscp",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_dscp_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_dscp_info)),
+ .help = dscp_help,
+ .print = dscp_print,
+ .save = dscp_save,
+ .x6_parse = dscp_parse,
+ .x6_fcheck = dscp_check,
+ .x6_options = dscp_opts,
+ .xlate = dscp_xlate,
+ },
+ {
+ .family = NFPROTO_IPV6,
+ .name = "dscp",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_dscp_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_dscp_info)),
+ .help = dscp_help,
+ .print = dscp_print,
+ .save = dscp_save,
+ .x6_parse = dscp_parse,
+ .x6_fcheck = dscp_check,
+ .x6_options = dscp_opts,
+ .xlate = dscp_xlate6,
+ },
};
void _init(void)
{
- xtables_register_match(&dscp_match);
+ xtables_register_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg));
}
diff --git a/extensions/libxt_dscp.t b/extensions/libxt_dscp.t
new file mode 100644
index 00000000..38d7f04e
--- /dev/null
+++ b/extensions/libxt_dscp.t
@@ -0,0 +1,10 @@
+:INPUT,FORWARD,OUTPUT
+-m dscp --dscp 0;=;OK
+-m dscp --dscp 0x3f;=;OK
+-m dscp --dscp -1;;FAIL
+-m dscp --dscp 0x40;;FAIL
+-m dscp --dscp 0x3f --dscp-class CS0;;FAIL
+-m dscp --dscp-class CS0;-m dscp --dscp 0x00;OK
+-m dscp --dscp-class BE;-m dscp --dscp 0x00;OK
+-m dscp --dscp-class EF;-m dscp --dscp 0x2e;OK
+-m dscp;;FAIL
diff --git a/extensions/libxt_ecn.c b/extensions/libxt_ecn.c
index 286782a3..aeba01b3 100644
--- a/extensions/libxt_ecn.c
+++ b/extensions/libxt_ecn.c
@@ -118,6 +118,36 @@ static void ecn_save(const void *ip, const struct xt_entry_match *match)
}
}
+static int ecn_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_ecn_info *einfo =
+ (const struct xt_ecn_info *)params->match->data;
+
+ if (!(einfo->operation & XT_ECN_OP_MATCH_IP))
+ return 0;
+
+ xt_xlate_add(xl, "ip ecn ");
+ if (einfo->invert)
+ xt_xlate_add(xl,"!= ");
+
+ switch (einfo->ip_ect) {
+ case 0:
+ xt_xlate_add(xl, "not-ect");
+ break;
+ case 1:
+ xt_xlate_add(xl, "ect1");
+ break;
+ case 2:
+ xt_xlate_add(xl, "ect0");
+ break;
+ case 3:
+ xt_xlate_add(xl, "ce");
+ break;
+ }
+ return 1;
+}
+
static struct xtables_match ecn_mt_reg = {
.name = "ecn",
.version = XTABLES_VERSION,
@@ -130,6 +160,7 @@ static struct xtables_match ecn_mt_reg = {
.x6_parse = ecn_parse,
.x6_fcheck = ecn_check,
.x6_options = ecn_opts,
+ .xlate = ecn_xlate,
};
void _init(void)
diff --git a/extensions/libxt_ecn.t b/extensions/libxt_ecn.t
new file mode 100644
index 00000000..b32aea30
--- /dev/null
+++ b/extensions/libxt_ecn.t
@@ -0,0 +1,5 @@
+:INPUT,FORWARD,OUTPUT
+-m ecn --ecn-tcp-cwr;;FAIL
+-p tcp -m ecn --ecn-tcp-cwr;=;OK
+-p tcp -m ecn --ecn-tcp-ece --ecn-tcp-cwr --ecn-ip-ect 2;=;OK
+-p tcp -m ecn ! --ecn-tcp-ece ! --ecn-tcp-cwr ! --ecn-ip-ect 2;=;OK
diff --git a/extensions/libxt_esp.c b/extensions/libxt_esp.c
index 294338b4..2c7ff942 100644
--- a/extensions/libxt_esp.c
+++ b/extensions/libxt_esp.c
@@ -21,6 +21,13 @@ static const struct xt_option_entry esp_opts[] = {
XTOPT_TABLEEND,
};
+static void esp_init(struct xt_entry_match *m)
+{
+ struct xt_esp *espinfo = (void *)m->data;
+
+ espinfo->spis[1] = ~0U;
+}
+
static void esp_parse(struct xt_option_call *cb)
{
struct xt_esp *espinfo = cb->data;
@@ -79,17 +86,37 @@ static void esp_save(const void *ip, const struct xt_entry_match *match)
}
+static int esp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_esp *espinfo = (struct xt_esp *)params->match->data;
+
+ if (!(espinfo->spis[0] == 0 && espinfo->spis[1] == 0xFFFFFFFF)) {
+ xt_xlate_add(xl, "esp spi%s",
+ (espinfo->invflags & XT_ESP_INV_SPI) ? " !=" : "");
+ if (espinfo->spis[0] != espinfo->spis[1])
+ xt_xlate_add(xl, " %u-%u", espinfo->spis[0],
+ espinfo->spis[1]);
+ else
+ xt_xlate_add(xl, " %u", espinfo->spis[0]);
+ }
+
+ return 1;
+}
+
static struct xtables_match esp_match = {
.family = NFPROTO_UNSPEC,
- .name = "esp",
- .version = XTABLES_VERSION,
+ .name = "esp",
+ .version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_esp)),
.userspacesize = XT_ALIGN(sizeof(struct xt_esp)),
.help = esp_help,
+ .init = esp_init,
.print = esp_print,
.save = esp_save,
.x6_parse = esp_parse,
.x6_options = esp_opts,
+ .xlate = esp_xlate,
};
void
diff --git a/extensions/libxt_esp.t b/extensions/libxt_esp.t
new file mode 100644
index 00000000..92c5779f
--- /dev/null
+++ b/extensions/libxt_esp.t
@@ -0,0 +1,8 @@
+:INPUT,FORWARD,OUTPUT
+-p esp -m esp --espspi 0;=;OK
+-p esp -m esp --espspi :32;-p esp -m esp --espspi 0:32;OK
+-p esp -m esp --espspi 0:4294967295;-p esp -m esp;OK
+-p esp -m esp ! --espspi 0:4294967294;=;OK
+-p esp -m esp --espspi -1;;FAIL
+-p esp -m esp;=;OK
+-m esp;;FAIL
diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c
index c5b8d779..7a1b37a9 100644
--- a/extensions/libxt_hashlimit.c
+++ b/extensions/libxt_hashlimit.c
@@ -18,12 +18,14 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <errno.h>
#include <xtables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_hashlimit.h>
#define XT_HASHLIMIT_BURST 5
-#define XT_HASHLIMIT_BURST_MAX 10000
+#define XT_HASHLIMIT_BURST_MAX_v1 10000
+#define XT_HASHLIMIT_BURST_MAX 1000000
#define XT_HASHLIMIT_BYTE_EXPIRE 15
#define XT_HASHLIMIT_BYTE_EXPIRE_BURST 60
@@ -98,7 +100,7 @@ static const struct xt_option_entry hashlimit_opts[] = {
{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
.type = XTTYPE_STRING},
{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
- .min = 1, .max = XT_HASHLIMIT_BURST_MAX, .flags = XTOPT_PUT,
+ .min = 1, .max = XT_HASHLIMIT_BURST_MAX_v1, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.burst)},
{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
@@ -121,6 +123,36 @@ static const struct xt_option_entry hashlimit_opts[] = {
#undef s
#define s struct xt_hashlimit_mtinfo1
+static const struct xt_option_entry hashlimit_mt_opts_v1[] = {
+ {.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
+ .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
+ {.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
+ .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
+ {.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
+ .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
+ {.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
+ {.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
+ {.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
+ {.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
+ .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+ XTOPT_POINTER(s, cfg.size)},
+ {.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
+ .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+ XTOPT_POINTER(s, cfg.max)},
+ {.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
+ .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+ XTOPT_POINTER(s, cfg.gc_interval)},
+ {.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
+ .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+ XTOPT_POINTER(s, cfg.expire)},
+ {.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
+ {.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
+ .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
+ XTOPT_TABLEEND,
+};
+#undef s
+
+#define s struct xt_hashlimit_mtinfo2
static const struct xt_option_entry hashlimit_mt_opts[] = {
{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
.type = XTTYPE_STRING, .flags = XTOPT_INVERT},
@@ -150,16 +182,40 @@ static const struct xt_option_entry hashlimit_mt_opts[] = {
};
#undef s
-static uint32_t cost_to_bytes(uint32_t cost)
+static int
+cfg_copy(struct hashlimit_cfg2 *to, const void *from, int revision)
+{
+ if (revision == 1) {
+ struct hashlimit_cfg1 *cfg = (struct hashlimit_cfg1 *)from;
+
+ to->mode = cfg->mode;
+ to->avg = cfg->avg;
+ to->burst = cfg->burst;
+ to->size = cfg->size;
+ to->max = cfg->max;
+ to->gc_interval = cfg->gc_interval;
+ to->expire = cfg->expire;
+ to->srcmask = cfg->srcmask;
+ to->dstmask = cfg->dstmask;
+ } else if (revision == 2) {
+ memcpy(to, from, sizeof(struct hashlimit_cfg2));
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static uint64_t cost_to_bytes(uint64_t cost)
{
- uint32_t r;
+ uint64_t r;
r = cost ? UINT32_MAX / cost : UINT32_MAX;
r = (r - 1) << XT_HASHLIMIT_BYTE_SHIFT;
return r;
}
-static uint64_t bytes_to_cost(uint32_t bytes)
+static uint64_t bytes_to_cost(uint64_t bytes)
{
uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT;
return UINT32_MAX / (r+1);
@@ -174,63 +230,84 @@ static uint32_t get_factor(int chr)
return 1;
}
+static void burst_error_v1(void)
+{
+ xtables_error(PARAMETER_PROBLEM, "bad value for option "
+ "\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX_v1);
+}
+
static void burst_error(void)
{
xtables_error(PARAMETER_PROBLEM, "bad value for option "
"\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX);
}
-static uint32_t parse_burst(const char *burst, struct xt_hashlimit_mtinfo1 *info)
+static uint64_t parse_burst(const char *burst, int revision)
{
uintmax_t v;
char *end;
-
- if (!xtables_strtoul(burst, &end, &v, 1, UINT32_MAX) ||
- (*end == 0 && v > XT_HASHLIMIT_BURST_MAX))
- burst_error();
+ uint64_t max = (revision == 1) ? UINT32_MAX : UINT64_MAX;
+ uint64_t burst_max = (revision == 1) ?
+ XT_HASHLIMIT_BURST_MAX_v1 : XT_HASHLIMIT_BURST_MAX;
+
+ if (!xtables_strtoul(burst, &end, &v, 1, max) ||
+ (*end == 0 && v > burst_max)) {
+ if (revision == 1)
+ burst_error_v1();
+ else
+ burst_error();
+ }
v *= get_factor(*end);
- if (v > UINT32_MAX)
+ if (v > max)
xtables_error(PARAMETER_PROBLEM, "bad value for option "
"\"--hashlimit-burst\", value \"%s\" too large "
- "(max %umb).", burst, UINT32_MAX/1024/1024);
+ "(max %lumb).", burst, max/1024/1024);
return v;
}
-static bool parse_bytes(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
+static bool parse_bytes(const char *rate, void *val, struct hashlimit_mt_udata *ud, int revision)
{
unsigned int factor = 1;
- uint64_t tmp;
- int r;
+ uint64_t tmp, r;
const char *mode = strstr(rate, "b/s");
+ uint64_t max = (revision == 1) ? UINT32_MAX : UINT64_MAX;
+
if (!mode || mode == rate)
return false;
mode--;
- r = atoi(rate);
+ r = atoll(rate);
if (r == 0)
return false;
factor = get_factor(*mode);
tmp = (uint64_t) r * factor;
- if (tmp > UINT32_MAX)
+ if (tmp > max)
xtables_error(PARAMETER_PROBLEM,
- "Rate value too large \"%llu\" (max %u)\n",
- (unsigned long long)tmp, UINT32_MAX);
+ "Rate value too large \"%llu\" (max %lu)\n",
+ (unsigned long long)tmp, max);
- *val = bytes_to_cost(tmp);
- if (*val == 0)
+ tmp = bytes_to_cost(tmp);
+ if (tmp == 0)
xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"\n", rate);
ud->mult = XT_HASHLIMIT_BYTE_EXPIRE;
+
+ if(revision == 1)
+ *((uint32_t*)val) = tmp;
+ else
+ *((uint64_t*)val) = tmp;
+
return true;
}
static
-int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
+int parse_rate(const char *rate, void *val, struct hashlimit_mt_udata *ud, int revision)
{
const char *delim;
- uint32_t r;
+ uint64_t tmp, r;
+ uint64_t scale = (revision == 1) ? XT_HASHLIMIT_SCALE : XT_HASHLIMIT_SCALE_v2;
ud->mult = 1; /* Seconds by default. */
delim = strchr(rate, '/');
@@ -249,17 +326,23 @@ int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
else
return 0;
}
- r = atoi(rate);
+ r = atoll(rate);
if (!r)
return 0;
- *val = XT_HASHLIMIT_SCALE * ud->mult / r;
- if (*val == 0)
+ tmp = scale * ud->mult / r;
+ if (tmp == 0)
/*
* The rate maps to infinity. (1/day is the minimum they can
* specify, so we are ok at that end).
*/
xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
+
+ if(revision == 1)
+ *((uint32_t*)val) = tmp;
+ else
+ *((uint64_t*)val) = tmp;
+
return 1;
}
@@ -272,7 +355,7 @@ static void hashlimit_init(struct xt_entry_match *m)
}
-static void hashlimit_mt4_init(struct xt_entry_match *match)
+static void hashlimit_mt4_init_v1(struct xt_entry_match *match)
{
struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
@@ -283,7 +366,7 @@ static void hashlimit_mt4_init(struct xt_entry_match *match)
info->cfg.dstmask = 32;
}
-static void hashlimit_mt6_init(struct xt_entry_match *match)
+static void hashlimit_mt6_init_v1(struct xt_entry_match *match)
{
struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
@@ -294,6 +377,28 @@ static void hashlimit_mt6_init(struct xt_entry_match *match)
info->cfg.dstmask = 128;
}
+static void hashlimit_mt4_init(struct xt_entry_match *match)
+{
+ struct xt_hashlimit_mtinfo2 *info = (void *)match->data;
+
+ info->cfg.mode = 0;
+ info->cfg.burst = XT_HASHLIMIT_BURST;
+ info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
+ info->cfg.srcmask = 32;
+ info->cfg.dstmask = 32;
+}
+
+static void hashlimit_mt6_init(struct xt_entry_match *match)
+{
+ struct xt_hashlimit_mtinfo2 *info = (void *)match->data;
+
+ info->cfg.mode = 0;
+ info->cfg.burst = XT_HASHLIMIT_BURST;
+ info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
+ info->cfg.srcmask = 128;
+ info->cfg.dstmask = 128;
+}
+
/* Parse a 'mode' parameter into the required bitmask */
static int parse_mode(uint32_t *mode, const char *option_arg)
{
@@ -330,7 +435,7 @@ static void hashlimit_parse(struct xt_option_call *cb)
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_UPTO:
- if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+ if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-upto", cb->arg);
break;
@@ -342,30 +447,71 @@ static void hashlimit_parse(struct xt_option_call *cb)
}
}
-static void hashlimit_mt_parse(struct xt_option_call *cb)
+static void hashlimit_mt_parse_v1(struct xt_option_call *cb)
{
struct xt_hashlimit_mtinfo1 *info = cb->data;
xtables_option_parse(cb);
switch (cb->entry->id) {
case O_BURST:
- info->cfg.burst = parse_burst(cb->arg, info);
+ info->cfg.burst = parse_burst(cb->arg, 1);
break;
case O_UPTO:
if (cb->invert)
info->cfg.mode |= XT_HASHLIMIT_INVERT;
- if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
+ if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 1))
info->cfg.mode |= XT_HASHLIMIT_BYTES;
- else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+ else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-upto", cb->arg);
break;
case O_ABOVE:
if (!cb->invert)
info->cfg.mode |= XT_HASHLIMIT_INVERT;
- if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
+ if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 1))
info->cfg.mode |= XT_HASHLIMIT_BYTES;
- else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+ else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
+ xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+ "--hashlimit-above", cb->arg);
+ break;
+ case O_MODE:
+ if (parse_mode(&info->cfg.mode, cb->arg) < 0)
+ xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+ "--hashlimit-mode", cb->arg);
+ break;
+ case O_SRCMASK:
+ info->cfg.srcmask = cb->val.hlen;
+ break;
+ case O_DSTMASK:
+ info->cfg.dstmask = cb->val.hlen;
+ break;
+ }
+}
+
+static void hashlimit_mt_parse(struct xt_option_call *cb)
+{
+ struct xt_hashlimit_mtinfo2 *info = cb->data;
+
+ xtables_option_parse(cb);
+ switch (cb->entry->id) {
+ case O_BURST:
+ info->cfg.burst = parse_burst(cb->arg, 2);
+ break;
+ case O_UPTO:
+ if (cb->invert)
+ info->cfg.mode |= XT_HASHLIMIT_INVERT;
+ if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2))
+ info->cfg.mode |= XT_HASHLIMIT_BYTES;
+ else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2))
+ xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+ "--hashlimit-upto", cb->arg);
+ break;
+ case O_ABOVE:
+ if (!cb->invert)
+ info->cfg.mode |= XT_HASHLIMIT_INVERT;
+ if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2))
+ info->cfg.mode |= XT_HASHLIMIT_BYTES;
+ else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2))
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-above", cb->arg);
break;
@@ -395,7 +541,7 @@ static void hashlimit_check(struct xt_fcheck_call *cb)
info->cfg.expire = udata->mult * 1000; /* from s to msec */
}
-static void hashlimit_mt_check(struct xt_fcheck_call *cb)
+static void hashlimit_mt_check_v1(struct xt_fcheck_call *cb)
{
const struct hashlimit_mt_udata *udata = cb->udata;
struct xt_hashlimit_mtinfo1 *info = cb->data;
@@ -411,7 +557,37 @@ static void hashlimit_mt_check(struct xt_fcheck_call *cb)
if (cb->xflags & F_BURST) {
if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
xtables_error(PARAMETER_PROBLEM,
- "burst cannot be smaller than %ub", cost_to_bytes(info->cfg.avg));
+ "burst cannot be smaller than %lub", cost_to_bytes(info->cfg.avg));
+
+ burst = info->cfg.burst;
+ burst /= cost_to_bytes(info->cfg.avg);
+ if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
+ burst++;
+ if (!(cb->xflags & F_HTABLE_EXPIRE))
+ info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
+ }
+ info->cfg.burst = burst;
+ } else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX_v1)
+ burst_error_v1();
+}
+
+static void hashlimit_mt_check(struct xt_fcheck_call *cb)
+{
+ const struct hashlimit_mt_udata *udata = cb->udata;
+ struct xt_hashlimit_mtinfo2 *info = cb->data;
+
+ if (!(cb->xflags & (F_UPTO | F_ABOVE)))
+ xtables_error(PARAMETER_PROBLEM,
+ "You have to specify --hashlimit");
+ if (!(cb->xflags & F_HTABLE_EXPIRE))
+ info->cfg.expire = udata->mult * 1000; /* from s to msec */
+
+ if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
+ uint32_t burst = 0;
+ if (cb->xflags & F_BURST) {
+ if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
+ xtables_error(PARAMETER_PROBLEM,
+ "burst cannot be smaller than %lub", cost_to_bytes(info->cfg.avg));
burst = info->cfg.burst;
burst /= cost_to_bytes(info->cfg.avg);
@@ -425,18 +601,25 @@ static void hashlimit_mt_check(struct xt_fcheck_call *cb)
burst_error();
}
-static const struct rates
-{
+struct rates {
const char *name;
- uint32_t mult;
-} rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
- { "hour", XT_HASHLIMIT_SCALE*60*60 },
- { "min", XT_HASHLIMIT_SCALE*60 },
- { "sec", XT_HASHLIMIT_SCALE } };
-
-static uint32_t print_rate(uint32_t period)
+ uint64_t mult;
+} rates_v1[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
+ { "hour", XT_HASHLIMIT_SCALE*60*60 },
+ { "min", XT_HASHLIMIT_SCALE*60 },
+ { "sec", XT_HASHLIMIT_SCALE } };
+
+static const struct rates rates[] = {
+ { "day", XT_HASHLIMIT_SCALE_v2*24*60*60 },
+ { "hour", XT_HASHLIMIT_SCALE_v2*60*60 },
+ { "min", XT_HASHLIMIT_SCALE_v2*60 },
+ { "sec", XT_HASHLIMIT_SCALE_v2 } };
+
+static uint32_t print_rate(uint32_t period, int revision)
{
unsigned int i;
+ const struct rates *_rates = (revision == 1) ? rates_v1 : rates;
+ uint64_t scale = (revision == 1) ? XT_HASHLIMIT_SCALE : XT_HASHLIMIT_SCALE_v2;
if (period == 0) {
printf(" %f", INFINITY);
@@ -444,13 +627,13 @@ static uint32_t print_rate(uint32_t period)
}
for (i = 1; i < ARRAY_SIZE(rates); ++i)
- if (period > rates[i].mult
- || rates[i].mult/period < rates[i].mult%period)
+ if (period > _rates[i].mult
+ || _rates[i].mult/period < _rates[i].mult%period)
break;
- printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
+ printf(" %lu/%s", _rates[i-1].mult / period, _rates[i-1].name);
/* return in msec */
- return rates[i-1].mult / XT_HASHLIMIT_SCALE * 1000;
+ return _rates[i-1].mult / scale * 1000;
}
static const struct {
@@ -462,7 +645,7 @@ static const struct {
{ "", 1 },
};
-static uint32_t print_bytes(uint32_t avg, uint32_t burst, const char *prefix)
+static uint32_t print_bytes(uint64_t avg, uint64_t burst, const char *prefix)
{
unsigned int i;
unsigned long long r;
@@ -523,7 +706,7 @@ static void hashlimit_print(const void *ip,
uint32_t quantum;
fputs(" limit: avg", stdout);
- quantum = print_rate(r->cfg.avg);
+ quantum = print_rate(r->cfg.avg, 1);
printf(" burst %u", r->cfg.burst);
fputs(" mode", stdout);
print_mode(r->cfg.mode, '-');
@@ -538,57 +721,89 @@ static void hashlimit_print(const void *ip,
}
static void
-hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
+hashlimit_mt_print(const struct hashlimit_cfg2 *cfg, unsigned int dmask, int revision)
{
uint32_t quantum;
- if (info->cfg.mode & XT_HASHLIMIT_INVERT)
+ if (cfg->mode & XT_HASHLIMIT_INVERT)
fputs(" limit: above", stdout);
else
fputs(" limit: up to", stdout);
- if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
- quantum = print_bytes(info->cfg.avg, info->cfg.burst, "");
+ if (cfg->mode & XT_HASHLIMIT_BYTES) {
+ quantum = print_bytes(cfg->avg, cfg->burst, "");
} else {
- quantum = print_rate(info->cfg.avg);
- printf(" burst %u", info->cfg.burst);
+ quantum = print_rate(cfg->avg, revision);
+ printf(" burst %llu", cfg->burst);
}
- if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
+ if (cfg->mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
fputs(" mode", stdout);
- print_mode(info->cfg.mode, '-');
+ print_mode(cfg->mode, '-');
}
- if (info->cfg.size != 0)
- printf(" htable-size %u", info->cfg.size);
- if (info->cfg.max != 0)
- printf(" htable-max %u", info->cfg.max);
- if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
- printf(" htable-gcinterval %u", info->cfg.gc_interval);
- if (info->cfg.expire != quantum)
- printf(" htable-expire %u", info->cfg.expire);
+ if (cfg->size != 0)
+ printf(" htable-size %u", cfg->size);
+ if (cfg->max != 0)
+ printf(" htable-max %u", cfg->max);
+ if (cfg->gc_interval != XT_HASHLIMIT_GCINTERVAL)
+ printf(" htable-gcinterval %u", cfg->gc_interval);
+ if (cfg->expire != quantum)
+ printf(" htable-expire %u", cfg->expire);
+
+ if (cfg->srcmask != dmask)
+ printf(" srcmask %u", cfg->srcmask);
+ if (cfg->dstmask != dmask)
+ printf(" dstmask %u", cfg->dstmask);
+}
+
+static void
+hashlimit_mt4_print_v1(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+ struct hashlimit_cfg2 cfg;
+ int ret;
+
+ ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
+
+ if (ret)
+ xtables_error(OTHER_PROBLEM, "unknown revision");
- if (info->cfg.srcmask != dmask)
- printf(" srcmask %u", info->cfg.srcmask);
- if (info->cfg.dstmask != dmask)
- printf(" dstmask %u", info->cfg.dstmask);
+ hashlimit_mt_print(&cfg, 32, 1);
}
static void
-hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
+hashlimit_mt6_print_v1(const void *ip, const struct xt_entry_match *match,
int numeric)
{
const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+ struct hashlimit_cfg2 cfg;
+ int ret;
+
+ ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
+
+ if (ret)
+ xtables_error(OTHER_PROBLEM, "unknown revision");
+
+ hashlimit_mt_print(&cfg, 128, 1);
+}
+
+static void
+hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
- hashlimit_mt_print(info, 32);
+ hashlimit_mt_print(&info->cfg, 32, 2);
}
static void
hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
- const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+ const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
- hashlimit_mt_print(info, 128);
+ hashlimit_mt_print(&info->cfg, 128, 2);
}
static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
@@ -597,7 +812,7 @@ static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
uint32_t quantum;
fputs(" --hashlimit", stdout);
- quantum = print_rate(r->cfg.avg);
+ quantum = print_rate(r->cfg.avg, 1);
printf(" --hashlimit-burst %u", r->cfg.burst);
fputs(" --hashlimit-mode", stdout);
@@ -616,59 +831,89 @@ static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
}
static void
-hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
+hashlimit_mt_save(const struct hashlimit_cfg2 *cfg, const char* name, unsigned int dmask, int revision)
{
uint32_t quantum;
- if (info->cfg.mode & XT_HASHLIMIT_INVERT)
+ if (cfg->mode & XT_HASHLIMIT_INVERT)
fputs(" --hashlimit-above", stdout);
else
fputs(" --hashlimit-upto", stdout);
- if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
- quantum = print_bytes(info->cfg.avg, info->cfg.burst, "--hashlimit-");
+ if (cfg->mode & XT_HASHLIMIT_BYTES) {
+ quantum = print_bytes(cfg->avg, cfg->burst, "--hashlimit-");
} else {
- quantum = print_rate(info->cfg.avg);
- printf(" --hashlimit-burst %u", info->cfg.burst);
+ quantum = print_rate(cfg->avg, revision);
+ printf(" --hashlimit-burst %llu", cfg->burst);
}
- if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
+ if (cfg->mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
fputs(" --hashlimit-mode", stdout);
- print_mode(info->cfg.mode, ',');
+ print_mode(cfg->mode, ',');
}
- printf(" --hashlimit-name %s", info->name);
+ printf(" --hashlimit-name %s", name);
+
+ if (cfg->size != 0)
+ printf(" --hashlimit-htable-size %u", cfg->size);
+ if (cfg->max != 0)
+ printf(" --hashlimit-htable-max %u", cfg->max);
+ if (cfg->gc_interval != XT_HASHLIMIT_GCINTERVAL)
+ printf(" --hashlimit-htable-gcinterval %u", cfg->gc_interval);
+ if (cfg->expire != quantum)
+ printf(" --hashlimit-htable-expire %u", cfg->expire);
+
+ if (cfg->srcmask != dmask)
+ printf(" --hashlimit-srcmask %u", cfg->srcmask);
+ if (cfg->dstmask != dmask)
+ printf(" --hashlimit-dstmask %u", cfg->dstmask);
+}
+
+static void
+hashlimit_mt4_save_v1(const void *ip, const struct xt_entry_match *match)
+{
+ const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+ struct hashlimit_cfg2 cfg;
+ int ret;
+
+ ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
- if (info->cfg.size != 0)
- printf(" --hashlimit-htable-size %u", info->cfg.size);
- if (info->cfg.max != 0)
- printf(" --hashlimit-htable-max %u", info->cfg.max);
- if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
- printf(" --hashlimit-htable-gcinterval %u", info->cfg.gc_interval);
- if (info->cfg.expire != quantum)
- printf(" --hashlimit-htable-expire %u", info->cfg.expire);
+ if (ret)
+ xtables_error(OTHER_PROBLEM, "unknown revision");
- if (info->cfg.srcmask != dmask)
- printf(" --hashlimit-srcmask %u", info->cfg.srcmask);
- if (info->cfg.dstmask != dmask)
- printf(" --hashlimit-dstmask %u", info->cfg.dstmask);
+ hashlimit_mt_save(&cfg, info->name, 32, 1);
}
static void
-hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
+hashlimit_mt6_save_v1(const void *ip, const struct xt_entry_match *match)
{
const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+ struct hashlimit_cfg2 cfg;
+ int ret;
+
+ ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
+
+ if (ret)
+ xtables_error(OTHER_PROBLEM, "unknown revision");
+
+ hashlimit_mt_save(&cfg, info->name, 128, 1);
+}
+
+static void
+hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
+{
+ const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
- hashlimit_mt_save(info, 32);
+ hashlimit_mt_save(&info->cfg, info->name, 32, 2);
}
static void
hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
{
- const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+ const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
- hashlimit_mt_save(info, 128);
+ hashlimit_mt_save(&info->cfg, info->name, 128, 2);
}
static struct xtables_match hashlimit_mt_reg[] = {
@@ -696,6 +941,38 @@ static struct xtables_match hashlimit_mt_reg[] = {
.size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
.help = hashlimit_mt_help,
+ .init = hashlimit_mt4_init_v1,
+ .x6_parse = hashlimit_mt_parse_v1,
+ .x6_fcheck = hashlimit_mt_check_v1,
+ .print = hashlimit_mt4_print_v1,
+ .save = hashlimit_mt4_save_v1,
+ .x6_options = hashlimit_mt_opts_v1,
+ .udata_size = sizeof(struct hashlimit_mt_udata),
+ },
+ {
+ .version = XTABLES_VERSION,
+ .name = "hashlimit",
+ .revision = 1,
+ .family = NFPROTO_IPV6,
+ .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
+ .userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
+ .help = hashlimit_mt_help,
+ .init = hashlimit_mt6_init_v1,
+ .x6_parse = hashlimit_mt_parse_v1,
+ .x6_fcheck = hashlimit_mt_check_v1,
+ .print = hashlimit_mt6_print_v1,
+ .save = hashlimit_mt6_save_v1,
+ .x6_options = hashlimit_mt_opts_v1,
+ .udata_size = sizeof(struct hashlimit_mt_udata),
+ },
+ {
+ .version = XTABLES_VERSION,
+ .name = "hashlimit",
+ .revision = 2,
+ .family = NFPROTO_IPV4,
+ .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo2)),
+ .userspacesize = offsetof(struct xt_hashlimit_mtinfo2, hinfo),
+ .help = hashlimit_mt_help,
.init = hashlimit_mt4_init,
.x6_parse = hashlimit_mt_parse,
.x6_fcheck = hashlimit_mt_check,
@@ -707,10 +984,10 @@ static struct xtables_match hashlimit_mt_reg[] = {
{
.version = XTABLES_VERSION,
.name = "hashlimit",
- .revision = 1,
+ .revision = 2,
.family = NFPROTO_IPV6,
- .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
- .userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
+ .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo2)),
+ .userspacesize = offsetof(struct xt_hashlimit_mtinfo2, hinfo),
.help = hashlimit_mt_help,
.init = hashlimit_mt6_init,
.x6_parse = hashlimit_mt_parse,
diff --git a/extensions/libxt_hashlimit.man b/extensions/libxt_hashlimit.man
index 17cb2b00..6aac3f28 100644
--- a/extensions/libxt_hashlimit.man
+++ b/extensions/libxt_hashlimit.man
@@ -65,7 +65,7 @@ matching on source port
matching on subnet
"10000 packets per minute for every /28 subnet (groups of 8 addresses)
in 10.0.0.0/8" =>
-\-s 10.0.0.8 \-\-hashlimit\-mask 28 \-\-hashlimit\-upto 10000/min
+\-s 10.0.0.0/8 \-\-hashlimit\-mask 28 \-\-hashlimit\-upto 10000/min
.TP
matching bytes per second
"flows exceeding 512kbyte/s" =>
diff --git a/extensions/libxt_hashlimit.t b/extensions/libxt_hashlimit.t
new file mode 100644
index 00000000..d27c8616
--- /dev/null
+++ b/extensions/libxt_hashlimit.t
@@ -0,0 +1,28 @@
+:INPUT,FORWARD,OUTPUT
+-m hashlimit --hashlimit-above 1/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-above 1000000/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-above 1/min --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-above 1/hour --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+# kernel says "xt_hashlimit: overflow, try lower: 864000000/5"
+-m hashlimit --hashlimit-above 1/day --hashlimit-burst 5 --hashlimit-name mini1;;FAIL
+-m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-upto 1000000/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-upto 1/min --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-upto 1/hour --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+# kernel says "xt_hashlimit: overflow, try lower: 864000000/5"
+-m hashlimit --hashlimit-upto 1/day --hashlimit-burst 5 --hashlimit-name mini1;;FAIL
+-m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-name mini1 --hashlimit-htable-expire 2000;=;OK
+-m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-mode srcip --hashlimit-name mini1 --hashlimit-htable-expire 2000;=;OK
+-m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-mode dstip --hashlimit-name mini1 --hashlimit-htable-expire 2000;=;OK
+-m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-mode dstip --hashlimit-name mini1 --hashlimit-htable-max 2000 --hashlimit-htable-expire 2000;=;OK
+-m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-mode dstip --hashlimit-name mini1 --hashlimit-htable-max 2000 --hashlimit-htable-gcinterval 60000 --hashlimit-htable-expire 2000;=;OK
+-m hashlimit --hashlimit-upto 1/sec --hashlimit-name mini1;-m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 5 --hashlimit-name mini1;OK
+-m hashlimit --hashlimit-upto 4kb/s --hashlimit-burst 400kb --hashlimit-name mini5;=;OK
+-m hashlimit --hashlimit-upto 10mb/s --hashlimit-name mini6;=;OK
+-m hashlimit --hashlimit-upto 123456b/s --hashlimit-burst 1mb --hashlimit-name mini7;=;OK
+# should work, it says "iptables v1.4.15: burst cannot be smaller than 96b"
+# ERROR: cannot load: iptables -A INPUT -m hashlimit --hashlimit-upto 96b/s --hashlimit-burst 5 --hashlimit-name mini1
+# -m hashlimit --hashlimit-upto 96b/s --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-name mini1;;FAIL
+-m hashlimit --hashlimit-upto 1/sec;;FAIL
+-m hashlimit;;FAIL
diff --git a/extensions/libxt_helper.c b/extensions/libxt_helper.c
index c9f9435e..2afbf996 100644
--- a/extensions/libxt_helper.c
+++ b/extensions/libxt_helper.c
@@ -45,6 +45,21 @@ static void helper_save(const void *ip, const struct xt_entry_match *match)
xtables_save_string(info->name);
}
+static int helper_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_helper_info *info = (const void *)params->match->data;
+
+ if (params->escape_quotes)
+ xt_xlate_add(xl, "ct helper%s \\\"%s\\\"",
+ info->invert ? " !=" : "", info->name);
+ else
+ xt_xlate_add(xl, "ct helper%s \"%s\"",
+ info->invert ? " !=" : "", info->name);
+
+ return 1;
+}
+
static struct xtables_match helper_match = {
.family = NFPROTO_UNSPEC,
.name = "helper",
@@ -55,6 +70,7 @@ static struct xtables_match helper_match = {
.save = helper_save,
.x6_parse = helper_parse,
.x6_options = helper_opts,
+ .xlate = helper_xlate,
};
void _init(void)
diff --git a/extensions/libxt_helper.t b/extensions/libxt_helper.t
new file mode 100644
index 00000000..8c8420ac
--- /dev/null
+++ b/extensions/libxt_helper.t
@@ -0,0 +1,6 @@
+:INPUT,FORWARD,OUTPUT
+-m helper --helper ftp;=;OK
+# should be OK?
+# ERROR: should fail: iptables -A INPUT -m helper --helper wrong
+# -m helper --helper wrong;;FAIL
+-m helper;;FAIL
diff --git a/extensions/libxt_ipcomp.c b/extensions/libxt_ipcomp.c
new file mode 100644
index 00000000..b5c43128
--- /dev/null
+++ b/extensions/libxt_ipcomp.c
@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_ipcomp.h>
+
+enum {
+ O_compSPI = 0,
+ O_compRES,
+};
+
+static void comp_help(void)
+{
+ printf(
+"comp match options:\n"
+"[!] --ipcompspi spi[:spi]\n"
+" match spi (range)\n");
+}
+
+static const struct xt_option_entry comp_opts[] = {
+ {.name = "ipcompspi", .id = O_compSPI, .type = XTTYPE_UINT32RC,
+ .flags = XTOPT_INVERT | XTOPT_PUT,
+ XTOPT_POINTER(struct xt_ipcomp, spis)},
+ {.name = "compres", .id = O_compRES, .type = XTTYPE_NONE},
+ XTOPT_TABLEEND,
+};
+#undef s
+
+static void comp_parse(struct xt_option_call *cb)
+{
+ struct xt_ipcomp *compinfo = cb->data;
+
+ xtables_option_parse(cb);
+ switch (cb->entry->id) {
+ case O_compSPI:
+ if (cb->nvals == 1)
+ compinfo->spis[1] = compinfo->spis[0];
+ if (cb->invert)
+ compinfo->invflags |= XT_IPCOMP_INV_SPI;
+ break;
+ case O_compRES:
+ compinfo->hdrres = 1;
+ break;
+ }
+}
+
+static void
+print_spis(const char *name, uint32_t min, uint32_t max,
+ int invert)
+{
+ const char *inv = invert ? "!" : "";
+
+ if (min != 0 || max != 0xFFFFFFFF || invert) {
+ if (min == max)
+ printf("%s:%s%u", name, inv, min);
+ else
+ printf("%ss:%s%u:%u", name, inv, min, max);
+ }
+}
+
+static void comp_print(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ const struct xt_ipcomp *comp = (struct xt_ipcomp *)match->data;
+
+ printf(" comp ");
+ print_spis("spi", comp->spis[0], comp->spis[1],
+ comp->invflags & XT_IPCOMP_INV_SPI);
+
+ if (comp->hdrres)
+ printf(" reserved");
+
+ if (comp->invflags & ~XT_IPCOMP_INV_MASK)
+ printf(" Unknown invflags: 0x%X",
+ comp->invflags & ~XT_IPCOMP_INV_MASK);
+}
+
+static void comp_save(const void *ip, const struct xt_entry_match *match)
+{
+ const struct xt_ipcomp *compinfo = (struct xt_ipcomp *)match->data;
+
+ if (!(compinfo->spis[0] == 0
+ && compinfo->spis[1] == 0xFFFFFFFF)) {
+ printf("%s --ipcompspi ",
+ (compinfo->invflags & XT_IPCOMP_INV_SPI) ? " !" : "");
+ if (compinfo->spis[0]
+ != compinfo->spis[1])
+ printf("%u:%u",
+ compinfo->spis[0],
+ compinfo->spis[1]);
+ else
+ printf("%u",
+ compinfo->spis[0]);
+ }
+
+ if (compinfo->hdrres != 0 )
+ printf(" --compres");
+}
+
+static int comp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_ipcomp *compinfo =
+ (struct xt_ipcomp *)params->match->data;
+
+ xt_xlate_add(xl, "comp cpi %s",
+ compinfo->invflags & XT_IPCOMP_INV_SPI ? "!= " : "");
+ if (compinfo->spis[0] != compinfo->spis[1])
+ xt_xlate_add(xl, "%u-%u", compinfo->spis[0],
+ compinfo->spis[1]);
+ else
+ xt_xlate_add(xl, "%u", compinfo->spis[0]);
+
+ return 1;
+}
+
+static struct xtables_match comp_mt_reg = {
+ .name = "ipcomp",
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_UNSPEC,
+ .size = XT_ALIGN(sizeof(struct xt_ipcomp)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_ipcomp)),
+ .help = comp_help,
+ .print = comp_print,
+ .save = comp_save,
+ .x6_parse = comp_parse,
+ .x6_options = comp_opts,
+ .xlate = comp_xlate,
+};
+
+void
+_init(void)
+{
+ xtables_register_match(&comp_mt_reg);
+};
+
diff --git a/extensions/libxt_ipcomp.c.man b/extensions/libxt_ipcomp.c.man
new file mode 100644
index 00000000..f3b17d21
--- /dev/null
+++ b/extensions/libxt_ipcomp.c.man
@@ -0,0 +1,7 @@
+This module matches the parameters in IPcomp header of IPsec packets.
+.TP
+[\fB!\fP] \fB\-\-ipcompspi\fP \fIspi\fP[\fB:\fP\fIspi\fP]
+Matches IPcomp header CPI value.
+.TP
+\fB\-\-compres\fP
+Matches if the reserved field is filled with zero.
diff --git a/extensions/libxt_iprange.c b/extensions/libxt_iprange.c
index 2c9ea992..8be24814 100644
--- a/extensions/libxt_iprange.c
+++ b/extensions/libxt_iprange.c
@@ -104,7 +104,8 @@ static void iprange_parse(struct xt_option_call *cb)
info->flags |= IPRANGE_SRC;
if (cb->invert)
info->flags |= IPRANGE_SRC_INV;
- iprange_parse_range(cb->arg, range, NFPROTO_IPV4, "--src-range");
+ iprange_parse_range(cb->arg, range,
+ NFPROTO_IPV4, "--src-range");
info->src.min_ip = range[0].ip;
info->src.max_ip = range[1].ip;
break;
@@ -112,7 +113,8 @@ static void iprange_parse(struct xt_option_call *cb)
info->flags |= IPRANGE_DST;
if (cb->invert)
info->flags |= IPRANGE_DST_INV;
- iprange_parse_range(cb->arg, range, NFPROTO_IPV4, "--dst-range");
+ iprange_parse_range(cb->arg, range,
+ NFPROTO_IPV4, "--dst-range");
info->dst.min_ip = range[0].ip;
info->dst.max_ip = range[1].ip;
break;
@@ -172,7 +174,7 @@ print_iprange(const struct ipt_iprange *range)
}
static void iprange_print(const void *ip, const struct xt_entry_match *match,
- int numeric)
+ int numeric)
{
const struct ipt_iprange_info *info = (const void *)match->data;
@@ -192,7 +194,7 @@ static void iprange_print(const void *ip, const struct xt_entry_match *match,
static void
iprange_mt4_print(const void *ip, const struct xt_entry_match *match,
- int numeric)
+ int numeric)
{
const struct xt_iprange_mtinfo *info = (const void *)match->data;
@@ -218,7 +220,7 @@ iprange_mt4_print(const void *ip, const struct xt_entry_match *match,
static void
iprange_mt6_print(const void *ip, const struct xt_entry_match *match,
- int numeric)
+ int numeric)
{
const struct xt_iprange_mtinfo *info = (const void *)match->data;
@@ -267,13 +269,15 @@ static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match)
if (info->flags & IPRANGE_SRC) {
if (info->flags & IPRANGE_SRC_INV)
printf(" !");
- printf(" --src-range %s", xtables_ipaddr_to_numeric(&info->src_min.in));
+ printf(" --src-range %s",
+ xtables_ipaddr_to_numeric(&info->src_min.in));
printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
}
if (info->flags & IPRANGE_DST) {
if (info->flags & IPRANGE_DST_INV)
printf(" !");
- printf(" --dst-range %s", xtables_ipaddr_to_numeric(&info->dst_min.in));
+ printf(" --dst-range %s",
+ xtables_ipaddr_to_numeric(&info->dst_min.in));
printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
}
}
@@ -285,17 +289,105 @@ static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match)
if (info->flags & IPRANGE_SRC) {
if (info->flags & IPRANGE_SRC_INV)
printf(" !");
- printf(" --src-range %s", xtables_ip6addr_to_numeric(&info->src_min.in6));
+ printf(" --src-range %s",
+ xtables_ip6addr_to_numeric(&info->src_min.in6));
printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
}
if (info->flags & IPRANGE_DST) {
if (info->flags & IPRANGE_DST_INV)
printf(" !");
- printf(" --dst-range %s", xtables_ip6addr_to_numeric(&info->dst_min.in6));
+ printf(" --dst-range %s",
+ xtables_ip6addr_to_numeric(&info->dst_min.in6));
printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
}
}
+static void
+print_iprange_xlate(const struct ipt_iprange *range,
+ struct xt_xlate *xl)
+{
+ const unsigned char *byte_min, *byte_max;
+
+ byte_min = (const unsigned char *)&range->min_ip;
+ byte_max = (const unsigned char *)&range->max_ip;
+ xt_xlate_add(xl, " %u.%u.%u.%u-%u.%u.%u.%u ",
+ byte_min[0], byte_min[1], byte_min[2], byte_min[3],
+ byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
+}
+
+static int iprange_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct ipt_iprange_info *info = (const void *)params->match->data;
+ char *space = "";
+
+ if (info->flags & IPRANGE_SRC) {
+ xt_xlate_add(xl, "ip saddr%s",
+ info->flags & IPRANGE_SRC_INV ? " !=" : "");
+ print_iprange_xlate(&info->src, xl);
+ space = " ";
+ }
+ if (info->flags & IPRANGE_DST) {
+ xt_xlate_add(xl, "%sip daddr%s", space,
+ info->flags & IPRANGE_DST_INV ? " !=" : "");
+ print_iprange_xlate(&info->dst, xl);
+ }
+
+ return 1;
+}
+
+static int iprange_mt4_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_iprange_mtinfo *info =
+ (const void *)params->match->data;
+ char *space = "";
+
+ if (info->flags & IPRANGE_SRC) {
+ xt_xlate_add(xl, "ip saddr%s %s",
+ info->flags & IPRANGE_SRC_INV ? " !=" : "",
+ xtables_ipaddr_to_numeric(&info->src_min.in));
+ xt_xlate_add(xl, "-%s",
+ xtables_ipaddr_to_numeric(&info->src_max.in));
+ space = " ";
+ }
+ if (info->flags & IPRANGE_DST) {
+ xt_xlate_add(xl, "%sip daddr%s %s", space,
+ info->flags & IPRANGE_DST_INV ? " !=" : "",
+ xtables_ipaddr_to_numeric(&info->dst_min.in));
+ xt_xlate_add(xl, "-%s",
+ xtables_ipaddr_to_numeric(&info->dst_max.in));
+ }
+
+ return 1;
+}
+
+static int iprange_mt6_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_iprange_mtinfo *info =
+ (const void *)params->match->data;
+ char *space = "";
+
+ if (info->flags & IPRANGE_SRC) {
+ xt_xlate_add(xl, "ip6 saddr%s %s",
+ info->flags & IPRANGE_SRC_INV ? " !=" : "",
+ xtables_ip6addr_to_numeric(&info->src_min.in6));
+ xt_xlate_add(xl, "-%s",
+ xtables_ip6addr_to_numeric(&info->src_max.in6));
+ space = " ";
+ }
+ if (info->flags & IPRANGE_DST) {
+ xt_xlate_add(xl, "%sip6 daddr%s %s", space,
+ info->flags & IPRANGE_DST_INV ? " !=" : "",
+ xtables_ip6addr_to_numeric(&info->dst_min.in6));
+ xt_xlate_add(xl, "-%s",
+ xtables_ip6addr_to_numeric(&info->dst_max.in6));
+ }
+
+ return 1;
+}
+
static struct xtables_match iprange_mt_reg[] = {
{
.version = XTABLES_VERSION,
@@ -310,6 +402,7 @@ static struct xtables_match iprange_mt_reg[] = {
.print = iprange_print,
.save = iprange_save,
.x6_options = iprange_mt_opts,
+ .xlate = iprange_xlate,
},
{
.version = XTABLES_VERSION,
@@ -324,6 +417,7 @@ static struct xtables_match iprange_mt_reg[] = {
.print = iprange_mt4_print,
.save = iprange_mt4_save,
.x6_options = iprange_mt_opts,
+ .xlate = iprange_mt4_xlate,
},
{
.version = XTABLES_VERSION,
@@ -338,6 +432,7 @@ static struct xtables_match iprange_mt_reg[] = {
.print = iprange_mt6_print,
.save = iprange_mt6_save,
.x6_options = iprange_mt_opts,
+ .xlate = iprange_mt6_xlate,
},
};
diff --git a/extensions/libxt_iprange.t b/extensions/libxt_iprange.t
new file mode 100644
index 00000000..6fd98be6
--- /dev/null
+++ b/extensions/libxt_iprange.t
@@ -0,0 +1,11 @@
+:INPUT,FORWARD,OUTPUT
+-m iprange --src-range 1.1.1.1-1.1.1.10;=;OK
+-m iprange ! --src-range 1.1.1.1-1.1.1.10;=;OK
+-m iprange --dst-range 1.1.1.1-1.1.1.10;=;OK
+-m iprange ! --dst-range 1.1.1.1-1.1.1.10;=;OK
+# it shows -A INPUT -m iprange --src-range 1.1.1.1-1.1.1.1, should we support this?
+# ERROR: should fail: iptables -A INPUT -m iprange --src-range 1.1.1.1
+# -m iprange --src-range 1.1.1.1;;FAIL
+# ERROR: should fail: iptables -A INPUT -m iprange --dst-range 1.1.1.1
+#-m iprange --dst-range 1.1.1.1;;FAIL
+-m iprange;;FAIL
diff --git a/extensions/libxt_length.c b/extensions/libxt_length.c
index 6ea76465..04eac4a5 100644
--- a/extensions/libxt_length.c
+++ b/extensions/libxt_length.c
@@ -56,6 +56,21 @@ static void length_save(const void *ip, const struct xt_entry_match *match)
printf("%u:%u", info->min, info->max);
}
+static int length_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_length_info *info = (void *)params->match->data;
+
+ xt_xlate_add(xl, "meta length %s", info->invert ? "!= " : "");
+ if (info->min == info->max)
+ xt_xlate_add(xl, "%u", info->min);
+ else
+ xt_xlate_add(xl, "%u-%u", info->min, info->max);
+
+ return 1;
+}
+
+
static struct xtables_match length_match = {
.family = NFPROTO_UNSPEC,
.name = "length",
@@ -67,6 +82,7 @@ static struct xtables_match length_match = {
.save = length_save,
.x6_parse = length_parse,
.x6_options = length_opts,
+ .xlate = length_xlate,
};
void _init(void)
diff --git a/extensions/libxt_length.t b/extensions/libxt_length.t
new file mode 100644
index 00000000..0b6624ee
--- /dev/null
+++ b/extensions/libxt_length.t
@@ -0,0 +1,10 @@
+:INPUT,FORWARD,OUTPUT
+-m length --length 1;=;OK
+-m length --length :2;-m length --length 0:2;OK
+-m length --length 0:3;=;OK
+-m length --length 4:;=;OK
+-m length --length 0:65535;=;OK
+-m length ! --length 0:65535;=;OK
+-m length --length 0:65536;;FAIL
+-m length --length -1:65535;;FAIL
+-m length;;FAIL
diff --git a/extensions/libxt_limit.c b/extensions/libxt_limit.c
index f75ef2f8..5cc95c2e 100644
--- a/extensions/libxt_limit.c
+++ b/extensions/libxt_limit.c
@@ -152,6 +152,44 @@ static void limit_save(const void *ip, const struct xt_entry_match *match)
printf(" --limit-burst %u", r->burst);
}
+static const struct rates rates_xlate[] = {
+ { "day", XT_LIMIT_SCALE * 24 * 60 * 60 },
+ { "hour", XT_LIMIT_SCALE * 60 * 60 },
+ { "minute", XT_LIMIT_SCALE * 60 },
+ { "second", XT_LIMIT_SCALE }
+};
+
+static void print_rate_xlate(uint32_t period, struct xt_xlate *xl)
+{
+ unsigned int i;
+
+ if (period == 0) {
+ xt_xlate_add(xl, " %f", INFINITY);
+ return;
+ }
+
+ for (i = 1; i < ARRAY_SIZE(rates); ++i)
+ if (period > rates_xlate[i].mult ||
+ rates_xlate[i].mult / period < rates_xlate[i].mult % period)
+ break;
+
+ xt_xlate_add(xl, " %u/%s", rates_xlate[i - 1].mult / period,
+ rates_xlate[i - 1].name);
+}
+
+static int limit_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_rateinfo *r = (const void *)params->match->data;
+
+ xt_xlate_add(xl, "limit rate");
+ print_rate_xlate(r->avg, xl);
+ if (r->burst != 0)
+ xt_xlate_add(xl, " burst %u packets", r->burst);
+
+ return 1;
+}
+
static struct xtables_match limit_match = {
.family = NFPROTO_UNSPEC,
.name = "limit",
@@ -164,6 +202,7 @@ static struct xtables_match limit_match = {
.print = limit_print,
.save = limit_save,
.x6_options = limit_opts,
+ .xlate = limit_xlate,
};
void _init(void)
diff --git a/extensions/libxt_limit.t b/extensions/libxt_limit.t
new file mode 100644
index 00000000..b0af6538
--- /dev/null
+++ b/extensions/libxt_limit.t
@@ -0,0 +1,6 @@
+:INPUT,FORWARD,OUTPUT
+-m limit --limit 1/sec;=;OK
+-m limit --limit 1/min;=;OK
+-m limit --limit 1000/hour;=;OK
+-m limit --limit 1000/day;=;OK
+-m limit --limit 1/sec --limit-burst 1;=;OK
diff --git a/extensions/libxt_mac.c b/extensions/libxt_mac.c
index f171d153..b6d717bc 100644
--- a/extensions/libxt_mac.c
+++ b/extensions/libxt_mac.c
@@ -50,11 +50,12 @@ static void
mac_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
const struct xt_mac_info *info = (void *)match->data;
+
printf(" MAC");
if (info->invert)
printf(" !");
-
+
print_mac(info->srcaddr);
}
@@ -69,9 +70,30 @@ static void mac_save(const void *ip, const struct xt_entry_match *match)
print_mac(info->srcaddr);
}
+static void print_mac_xlate(const unsigned char *macaddress,
+ struct xt_xlate *xl)
+{
+ unsigned int i;
+
+ xt_xlate_add(xl, "%02x", macaddress[0]);
+ for (i = 1; i < ETH_ALEN; ++i)
+ xt_xlate_add(xl, ":%02x", macaddress[i]);
+}
+
+static int mac_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_mac_info *info = (void *)params->match->data;
+
+ xt_xlate_add(xl, "ether saddr%s ", info->invert ? " !=" : "");
+ print_mac_xlate(info->srcaddr, xl);
+
+ return 1;
+}
+
static struct xtables_match mac_match = {
.family = NFPROTO_UNSPEC,
- .name = "mac",
+ .name = "mac",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_mac_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_mac_info)),
@@ -80,6 +102,7 @@ static struct xtables_match mac_match = {
.print = mac_print,
.save = mac_save,
.x6_options = mac_opts,
+ .xlate = mac_xlate,
};
void _init(void)
diff --git a/extensions/libxt_mac.t b/extensions/libxt_mac.t
new file mode 100644
index 00000000..a5ec81d8
--- /dev/null
+++ b/extensions/libxt_mac.t
@@ -0,0 +1,5 @@
+:INPUT,FORWARD
+-m mac --mac-source 42:01:02:03:04:05;=;OK
+-m mac --mac-source 42:01:02:03:04;=;FAIL
+-m mac --mac-source 42:01:02:03:04:05:06;=;FAIL
+-m mac;;FAIL
diff --git a/extensions/libxt_mangle.c b/extensions/libxt_mangle.c
new file mode 100644
index 00000000..360742b6
--- /dev/null
+++ b/extensions/libxt_mangle.c
@@ -0,0 +1,396 @@
+/*
+ * 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 <bdschuym@pandora.be>
+ * Port to libxtables: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <errno.h>
+#include <netinet/ether.h>
+
+#include <xtables.h>
+#include <linux/netfilter_arp/arpt_mangle.h>
+
+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"
+ );
+}
+
+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 },
+ { .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,
+};
+
+
+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 in_addr *addr;
+ struct addrinfo hints;
+ struct addrinfo *res, *p;
+ int err;
+ unsigned int i;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_RAW;
+
+ *naddr = 0;
+ err = getaddrinfo(name, NULL, &hints, &res);
+ if (err != 0)
+ return NULL;
+ else {
+ for (p = res; p != NULL; p = p->ai_next)
+ (*naddr)++;
+ addr = xtables_calloc(*naddr, sizeof(struct in_addr));
+ for (i = 0, p = res; p != NULL; p = p->ai_next)
+ memcpy(&addr[i++],
+ &((const struct sockaddr_in *)p->ai_addr)->sin_addr,
+ sizeof(struct in_addr));
+ freeaddrinfo(res);
+ 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);
+}
diff --git a/extensions/libxt_mark.c b/extensions/libxt_mark.c
index 7f8c995c..e1d00de9 100644
--- a/extensions/libxt_mark.c
+++ b/extensions/libxt_mark.c
@@ -75,7 +75,7 @@ mark_print(const void *ip, const struct xt_entry_match *match, int numeric)
if (info->invert)
printf(" !");
-
+
print_mark(info->mark, info->mask);
}
@@ -97,11 +97,53 @@ mark_save(const void *ip, const struct xt_entry_match *match)
if (info->invert)
printf(" !");
-
+
printf(" --mark");
print_mark(info->mark, info->mask);
}
+static void
+print_mark_xlate(struct xt_xlate *xl, unsigned int mark,
+ unsigned int mask, uint32_t op)
+{
+ if (mask != 0xffffffffU)
+ xt_xlate_add(xl, " and 0x%x %s 0x%x", mask,
+ op == XT_OP_EQ ? "==" : "!=", mark);
+ else
+ xt_xlate_add(xl, " %s0x%x",
+ op == XT_OP_EQ ? "" : "!= ", mark);
+}
+
+static int mark_mt_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_mark_mtinfo1 *info = (const void *)params->match->data;
+ enum xt_op op = XT_OP_EQ;
+
+ if (info->invert)
+ op = XT_OP_NEQ;
+
+ xt_xlate_add(xl, "mark");
+ print_mark_xlate(xl, info->mark, info->mask, op);
+
+ return 1;
+}
+
+static int mark_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_mark_info *info = (const void *)params->match->data;
+ enum xt_op op = XT_OP_EQ;
+
+ if (info->invert)
+ op = XT_OP_NEQ;
+
+ xt_xlate_add(xl, "mark");
+ print_mark_xlate(xl, info->mark, info->mask, op);
+
+ return 1;
+}
+
static struct xtables_match mark_mt_reg[] = {
{
.family = NFPROTO_UNSPEC,
@@ -115,6 +157,7 @@ static struct xtables_match mark_mt_reg[] = {
.save = mark_save,
.x6_parse = mark_parse,
.x6_options = mark_mt_opts,
+ .xlate = mark_xlate,
},
{
.version = XTABLES_VERSION,
@@ -128,6 +171,7 @@ static struct xtables_match mark_mt_reg[] = {
.save = mark_mt_save,
.x6_parse = mark_mt_parse,
.x6_options = mark_mt_opts,
+ .xlate = mark_mt_xlate,
},
};
diff --git a/extensions/libxt_mark.t b/extensions/libxt_mark.t
new file mode 100644
index 00000000..7c005379
--- /dev/null
+++ b/extensions/libxt_mark.t
@@ -0,0 +1,7 @@
+:INPUT,FORWARD,OUTPUT
+-m mark --mark 0xfeedcafe/0xfeedcafe;=;OK
+-m mark --mark 0;=;OK
+-m mark --mark 4294967295;-m mark --mark 0xffffffff;OK
+-m mark --mark 4294967296;;FAIL
+-m mark --mark -1;;FAIL
+-m mark;;FAIL
diff --git a/extensions/libxt_multiport.c b/extensions/libxt_multiport.c
index 03af5a96..07ad4cfd 100644
--- a/extensions/libxt_multiport.c
+++ b/extensions/libxt_multiport.c
@@ -108,7 +108,6 @@ parse_multi_ports_v1(const char *portstring,
{
char *buffer, *cp, *next, *range;
unsigned int i;
- uint16_t m;
buffer = strdup(portstring);
if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed");
@@ -133,7 +132,6 @@ parse_multi_ports_v1(const char *portstring,
if (multiinfo->ports[i-1] >= multiinfo->ports[i])
xtables_error(PARAMETER_PROBLEM,
"invalid portrange specified");
- m <<= 1;
}
}
multiinfo->count = i;
@@ -468,6 +466,110 @@ static void multiport_save6_v1(const void *ip_void,
__multiport_save_v1(match, ip->proto);
}
+static int __multiport_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_multiport *multiinfo
+ = (const struct xt_multiport *)params->match->data;
+ unsigned int i;
+
+ switch (multiinfo->flags) {
+ case XT_MULTIPORT_SOURCE:
+ xt_xlate_add(xl, " sport ");
+ break;
+ case XT_MULTIPORT_DESTINATION:
+ xt_xlate_add(xl, " dport ");
+ break;
+ case XT_MULTIPORT_EITHER:
+ return 0;
+ }
+
+ if (multiinfo->count > 1)
+ xt_xlate_add(xl, "{ ");
+
+ for (i = 0; i < multiinfo->count; i++)
+ xt_xlate_add(xl, "%s%u", i ? "," : "", multiinfo->ports[i]);
+
+ if (multiinfo->count > 1)
+ xt_xlate_add(xl, "}");
+
+ return 1;
+}
+
+static int multiport_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ uint8_t proto = ((const struct ipt_ip *)params->ip)->proto;
+
+ xt_xlate_add(xl, "%s", proto_to_name(proto));
+ return __multiport_xlate(xl, params);
+}
+
+static int multiport_xlate6(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ uint8_t proto = ((const struct ip6t_ip6 *)params->ip)->proto;
+
+ xt_xlate_add(xl, "%s", proto_to_name(proto));
+ return __multiport_xlate(xl, params);
+}
+
+static int __multiport_xlate_v1(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_multiport_v1 *multiinfo =
+ (const struct xt_multiport_v1 *)params->match->data;
+ unsigned int i;
+
+ switch (multiinfo->flags) {
+ case XT_MULTIPORT_SOURCE:
+ xt_xlate_add(xl, " sport ");
+ break;
+ case XT_MULTIPORT_DESTINATION:
+ xt_xlate_add(xl, " dport ");
+ break;
+ case XT_MULTIPORT_EITHER:
+ return 0;
+ }
+
+ if (multiinfo->invert)
+ xt_xlate_add(xl, "!= ");
+
+ if (multiinfo->count > 2 ||
+ (multiinfo->count > 1 && !multiinfo->pflags[0]))
+ xt_xlate_add(xl, "{ ");
+
+ for (i = 0; i < multiinfo->count; i++) {
+ xt_xlate_add(xl, "%s%u", i ? "," : "", multiinfo->ports[i]);
+ if (multiinfo->pflags[i])
+ xt_xlate_add(xl, "-%u", multiinfo->ports[++i]);
+ }
+
+ if (multiinfo->count > 2 ||
+ (multiinfo->count > 1 && !multiinfo->pflags[0]))
+ xt_xlate_add(xl, "}");
+
+ return 1;
+}
+
+static int multiport_xlate_v1(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ uint8_t proto = ((const struct ipt_ip *)params->ip)->proto;
+
+ xt_xlate_add(xl, "%s", proto_to_name(proto));
+ return __multiport_xlate_v1(xl, params);
+}
+
+static int multiport_xlate6_v1(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ uint8_t proto = ((const struct ip6t_ip6 *)params->ip)->proto;
+
+ xt_xlate_add(xl, "%s", proto_to_name(proto));
+ return __multiport_xlate_v1(xl, params);
+}
+
static struct xtables_match multiport_mt_reg[] = {
{
.family = NFPROTO_IPV4,
@@ -482,6 +584,7 @@ static struct xtables_match multiport_mt_reg[] = {
.print = multiport_print,
.save = multiport_save,
.x6_options = multiport_opts,
+ .xlate = multiport_xlate,
},
{
.family = NFPROTO_IPV6,
@@ -496,6 +599,7 @@ static struct xtables_match multiport_mt_reg[] = {
.print = multiport_print6,
.save = multiport_save6,
.x6_options = multiport_opts,
+ .xlate = multiport_xlate6,
},
{
.family = NFPROTO_IPV4,
@@ -510,6 +614,7 @@ static struct xtables_match multiport_mt_reg[] = {
.print = multiport_print_v1,
.save = multiport_save_v1,
.x6_options = multiport_opts,
+ .xlate = multiport_xlate_v1,
},
{
.family = NFPROTO_IPV6,
@@ -524,6 +629,7 @@ static struct xtables_match multiport_mt_reg[] = {
.print = multiport_print6_v1,
.save = multiport_save6_v1,
.x6_options = multiport_opts,
+ .xlate = multiport_xlate6_v1,
},
};
diff --git a/extensions/libxt_multiport.t b/extensions/libxt_multiport.t
new file mode 100644
index 00000000..e9b80a4e
--- /dev/null
+++ b/extensions/libxt_multiport.t
@@ -0,0 +1,23 @@
+:INPUT,FORWARD,OUTPUT
+-p tcp -m multiport --sports 53,1024:65535;=;OK
+-p tcp -m multiport --dports 53,1024:65535;=;OK
+-p udp -m multiport --sports 53,1024:65535;=;OK
+-p udp -m multiport --dports 53,1024:65535;=;OK
+-p udp -m multiport --ports 53,1024:65535;=;OK
+-p udp -m multiport --ports 53,1024:65535;=;OK
+-p sctp -m multiport --sports 53,1024:65535;=;OK
+-p sctp -m multiport --dports 53,1024:65535;=;OK
+-p dccp -m multiport --sports 53,1024:65535;=;OK
+-p dccp -m multiport --dports 53,1024:65535;=;OK
+-p udplite -m multiport --sports 53,1024:65535;=;OK
+-p udplite -m multiport --dports 53,1024:65535;=;OK
+-p tcp -m multiport --sports 1024:65536;;FAIL
+-p udp -m multiport --sports 1024:65536;;FAIL
+-p tcp -m multiport --ports 1024:65536;;FAIL
+-p udp -m multiport --ports 1024:65536;;FAIL
+-p tcp -m multiport --ports 1,2,3,4,6,7,8,9,10,11,12,13,14,15;=;OK
+# fix manpage, it says "up to 15 ports supported"
+# ERROR: should fail: iptables -A INPUT -p tcp -m multiport --ports 1,2,3,4,6,7,8,9,10,11,12,13,14,15,16
+# -p tcp -m multiport --ports 1,2,3,4,6,7,8,9,10,11,12,13,14,15,16;;FAIL
+-p tcp --multiport;;FAIL
+-m multiport;;FAIL
diff --git a/extensions/libxt_nfacct.t b/extensions/libxt_nfacct.t
new file mode 100644
index 00000000..3419b4ce
--- /dev/null
+++ b/extensions/libxt_nfacct.t
@@ -0,0 +1,10 @@
+:INPUT,FORWARD,OUTPUT
+@nfacct add test
+#
+# extra space in iptables-save output, fix it
+#
+# ERROR: cannot load: iptables -A INPUT -m nfacct --nfacct-name test
+#-m nfacct --nfacct-name test;=;OK
+-m nfacct --nfacct-name wrong;;FAIL
+-m nfacct;;FAIL
+@nfacct del test
diff --git a/extensions/libxt_osf.c b/extensions/libxt_osf.c
index 52dba474..496b4805 100644
--- a/extensions/libxt_osf.c
+++ b/extensions/libxt_osf.c
@@ -14,7 +14,7 @@
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
diff --git a/extensions/libxt_osf.t b/extensions/libxt_osf.t
new file mode 100644
index 00000000..ede6d32c
--- /dev/null
+++ b/extensions/libxt_osf.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD
+-m osf --genre linux --ttl 0 --log 0;;FAIL
+-p tcp -m osf --genre linux --ttl 0 --log 0;=;OK
+-p tcp -m osf --genre linux --ttl 3 --log 0;;FAIL
diff --git a/extensions/libxt_owner.c b/extensions/libxt_owner.c
index d9adc12e..87e4df31 100644
--- a/extensions/libxt_owner.c
+++ b/extensions/libxt_owner.c
@@ -492,6 +492,56 @@ static void owner_mt_save(const void *ip, const struct xt_entry_match *match)
owner_mt_print_item(info, "--gid-owner", XT_OWNER_GID, true);
}
+static int
+owner_mt_print_uid_xlate(const struct xt_owner_match_info *info,
+ struct xt_xlate *xl)
+{
+ xt_xlate_add(xl, "skuid%s ", info->invert ? " !=" : "");
+
+ if (info->uid_min != info->uid_max)
+ xt_xlate_add(xl, "%u-%u", (unsigned int)info->uid_min,
+ (unsigned int)info->uid_max);
+ else
+ xt_xlate_add(xl, "%u", (unsigned int)info->uid_min);
+
+ return 1;
+}
+
+static int
+owner_mt_print_gid_xlate(const struct xt_owner_match_info *info,
+ struct xt_xlate *xl)
+{
+ xt_xlate_add(xl, "skgid%s ", info->invert ? " !=" : "");
+
+ if (info->gid_min != info->gid_max)
+ xt_xlate_add(xl, "%u-%u", (unsigned int)info->gid_min,
+ (unsigned int)info->gid_max);
+ else
+ xt_xlate_add(xl, "%u", (unsigned int)info->gid_min);
+
+ return 1;
+}
+
+static int owner_mt_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_owner_match_info *info = (void *)params->match->data;
+ int ret;
+
+ switch (info->match) {
+ case XT_OWNER_UID:
+ ret = owner_mt_print_uid_xlate(info, xl);
+ break;
+ case XT_OWNER_GID:
+ ret = owner_mt_print_gid_xlate(info, xl);
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
static struct xtables_match owner_mt_reg[] = {
{
.version = XTABLES_VERSION,
@@ -534,6 +584,7 @@ static struct xtables_match owner_mt_reg[] = {
.print = owner_mt_print,
.save = owner_mt_save,
.x6_options = owner_mt_opts,
+ .xlate = owner_mt_xlate,
},
};
diff --git a/extensions/libxt_owner.t b/extensions/libxt_owner.t
new file mode 100644
index 00000000..aec30b65
--- /dev/null
+++ b/extensions/libxt_owner.t
@@ -0,0 +1,12 @@
+:OUTPUT,POSTROUTING
+*mangle
+-m owner --uid-owner root;-m owner --uid-owner 0;OK
+-m owner --uid-owner 0-10;=;OK
+-m owner --gid-owner root;-m owner --gid-owner 0;OK
+-m owner --gid-owner 0-10;=;OK
+-m owner --uid-owner root --gid-owner root;-m owner --uid-owner 0 --gid-owner 0;OK
+-m owner --uid-owner 0-10 --gid-owner 0-10;=;OK
+-m owner ! --uid-owner root;-m owner ! --uid-owner 0;OK
+-m owner --socket-exists;=;OK
+:INPUT
+-m owner --uid-owner root;;FAIL
diff --git a/extensions/libxt_physdev.man b/extensions/libxt_physdev.man
index 53beb2e5..06b778af 100644
--- a/extensions/libxt_physdev.man
+++ b/extensions/libxt_physdev.man
@@ -15,21 +15,13 @@ interface which begins with this name will match. If the packet didn't arrive
through a bridge device, this packet won't match this option, unless '!' is used.
.TP
[\fB!\fP] \fB\-\-physdev\-out\fP \fIname\fP
-Name of a bridge port via which a packet is going to be sent (for packets
+Name of a bridge port via which a packet is going to be sent (for bridged packets
entering the
-.BR FORWARD ,
-.B OUTPUT
+.BR FORWARD
and
.B POSTROUTING
chains). If the interface name ends in a "+", then any
-interface which begins with this name will match. Note that in the
-.BR nat " and " mangle
-.B OUTPUT
-chains one cannot match on the bridge output port, however one can in the
-.B "filter OUTPUT"
-chain. If the packet won't leave by a bridge device or if it is yet unknown what
-the output device will be, then the packet won't match this option,
-unless '!' is used.
+interface which begins with this name will match.
.TP
[\fB!\fP] \fB\-\-physdev\-is\-in\fP
Matches if the packet has entered through a bridge interface.
diff --git a/extensions/libxt_physdev.t b/extensions/libxt_physdev.t
new file mode 100644
index 00000000..1fab7e19
--- /dev/null
+++ b/extensions/libxt_physdev.t
@@ -0,0 +1,14 @@
+:INPUT,FORWARD
+-m physdev --physdev-in lo;=;OK
+-m physdev --physdev-is-in --physdev-in lo;=;OK
+:OUTPUT,FORWARD
+# xt_physdev: using --physdev-out in the OUTPUT, FORWARD and POSTROUTING chains for non-bridged traffic is not supported anymore.
+# ERROR: should fail: iptables -A FORWARD -m physdev --physdev-out lo
+#-m physdev --physdev-out lo;;FAIL
+# ERROR: cannot load: iptables -A OUTPUT -m physdev --physdev-is-out --physdev-out lo
+#-m physdev --physdev-is-out --physdev-out lo;=;OK
+:FORWARD
+-m physdev --physdev-in lo --physdev-is-bridged;=;OK
+:POSTROUTING
+*mangle
+-m physdev --physdev-out lo --physdev-is-bridged;=;OK
diff --git a/extensions/libxt_pkttype.c b/extensions/libxt_pkttype.c
index 1ed3b445..bf6f5b96 100644
--- a/extensions/libxt_pkttype.c
+++ b/extensions/libxt_pkttype.c
@@ -21,6 +21,11 @@ struct pkttypes {
const char *help;
};
+struct pkttypes_xlate {
+ const char *name;
+ unsigned char pkttype;
+};
+
static const struct pkttypes supported_types[] = {
{"unicast", PACKET_HOST, 1, "to us"},
{"broadcast", PACKET_BROADCAST, 1, "to all"},
@@ -115,6 +120,37 @@ static void pkttype_save(const void *ip, const struct xt_entry_match *match)
print_pkttype(info);
}
+static const struct pkttypes_xlate supported_types_xlate[] = {
+ {"unicast", PACKET_HOST},
+ {"broadcast", PACKET_BROADCAST},
+ {"multicast", PACKET_MULTICAST},
+};
+
+static void print_pkttype_xlate(const struct xt_pkttype_info *info,
+ struct xt_xlate *xl)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(supported_types_xlate); ++i) {
+ if (supported_types_xlate[i].pkttype == info->pkttype) {
+ xt_xlate_add(xl, "%s", supported_types_xlate[i].name);
+ return;
+ }
+ }
+ xt_xlate_add(xl, "%d", info->pkttype);
+}
+
+static int pkttype_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_pkttype_info *info = (const void *)params->match->data;
+
+ xt_xlate_add(xl, "pkttype%s ", info->invert ? " !=" : "");
+ print_pkttype_xlate(info, xl);
+
+ return 1;
+}
+
static struct xtables_match pkttype_match = {
.family = NFPROTO_UNSPEC,
.name = "pkttype",
@@ -126,6 +162,7 @@ static struct xtables_match pkttype_match = {
.save = pkttype_save,
.x6_parse = pkttype_parse,
.x6_options = pkttype_opts,
+ .xlate = pkttype_xlate,
};
void _init(void)
diff --git a/extensions/libxt_pkttype.t b/extensions/libxt_pkttype.t
new file mode 100644
index 00000000..d93baeaf
--- /dev/null
+++ b/extensions/libxt_pkttype.t
@@ -0,0 +1,6 @@
+:INPUT,FORWARD,OUTPUT
+-m pkttype --pkt-type unicast;=;OK
+-m pkttype --pkt-type broadcast;=;OK
+-m pkttype --pkt-type multicast;=;OK
+-m pkttype --pkt-type wrong;;FAIL
+-m pkttype;;FAIL
diff --git a/extensions/libxt_policy.t b/extensions/libxt_policy.t
new file mode 100644
index 00000000..24a3e2f4
--- /dev/null
+++ b/extensions/libxt_policy.t
@@ -0,0 +1,5 @@
+:INPUT,FORWARD
+-m policy --dir in --pol ipsec;=;OK
+-m policy --dir in --pol ipsec --strict;;FAIL
+-m policy --dir in --pol ipsec --strict --reqid 1 --spi 0x1 --proto esp --mode tunnel --tunnel-dst 10.0.0.0/8 --tunnel-src 10.0.0.0/8 --next --reqid 2;=;OK
+-m policy --dir in --pol ipsec --strict --reqid 1 --spi 0x1 --proto esp --tunnel-dst 10.0.0.0/8;;FAIL
diff --git a/extensions/libxt_quota.c b/extensions/libxt_quota.c
index 26fba0bb..192cc717 100644
--- a/extensions/libxt_quota.c
+++ b/extensions/libxt_quota.c
@@ -37,7 +37,7 @@ quota_save(const void *ip, const struct xt_entry_match *match)
const struct xt_quota_info *q = (const void *)match->data;
if (q->flags & XT_QUOTA_INVERT)
- printf("! ");
+ printf(" !");
printf(" --quota %llu", (unsigned long long) q->quota);
}
@@ -51,6 +51,17 @@ static void quota_parse(struct xt_option_call *cb)
info->quota = cb->val.u64;
}
+static int quota_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_quota_info *q = (void *)params->match->data;
+
+ xt_xlate_add(xl, "quota %s%llu bytes",
+ q->flags & XT_QUOTA_INVERT ? "over " : "",
+ (unsigned long long) q->quota);
+ return 1;
+}
+
static struct xtables_match quota_match = {
.family = NFPROTO_UNSPEC,
.name = "quota",
@@ -62,6 +73,7 @@ static struct xtables_match quota_match = {
.save = quota_save,
.x6_parse = quota_parse,
.x6_options = quota_opts,
+ .xlate = quota_xlate,
};
void
diff --git a/extensions/libxt_quota.t b/extensions/libxt_quota.t
new file mode 100644
index 00000000..c5684279
--- /dev/null
+++ b/extensions/libxt_quota.t
@@ -0,0 +1,7 @@
+:INPUT,FORWARD,OUTPUT
+-m quota --quota 0;=;OK
+-m quota ! --quota 0;=;OK
+-m quota --quota 18446744073709551615;=;OK
+-m quota ! --quota 18446744073709551615;=;OK
+-m quota --quota 18446744073709551616;;FAIL
+-m quota;;FAIL
diff --git a/extensions/libxt_rateest.t b/extensions/libxt_rateest.t
new file mode 100644
index 00000000..c53b4b62
--- /dev/null
+++ b/extensions/libxt_rateest.t
@@ -0,0 +1,16 @@
+:INPUT,FORWARD,OUTPUT
+@iptables -I INPUT -j RATEEST --rateest-name RE1 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms
+-m rateest --rateest RE1 --rateest-lt --rateest-bps 8bit;=;OK
+-m rateest --rateest RE1 --rateest-eq --rateest-pps 5;=;OK
+-m rateest --rateest RE1 --rateest-gt --rateest-bps 5kbit;-m rateest --rateest RE1 --rateest-gt --rateest-bps 5000bit;OK
+-m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-lt --rateest-bps2 16bit;=;OK
+@iptables -I INPUT -j RATEEST --rateest-name RE2 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms
+-m rateest --rateest1 RE1 --rateest-lt --rateest-bps --rateest2 RE2;=;OK
+-m rateest --rateest-delta --rateest1 RE1 --rateest-pps1 0 --rateest-lt --rateest-pps2 42 --rateest2 RE2;=;OK
+-m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-eq --rateest-bps2 16bit;=;OK
+-m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-gt --rateest-bps2 16bit;=;OK
+-m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-lt --rateest-pps2 9;=;OK
+-m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-eq --rateest-pps2 9;=;OK
+-m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-gt --rateest-pps2 9;=;OK
+@iptables -D INPUT -j RATEEST --rateest-name RE1 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms
+@iptables -D INPUT -j RATEEST --rateest-name RE2 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms
diff --git a/extensions/libxt_recent.c b/extensions/libxt_recent.c
index b3510d99..e1801f1c 100644
--- a/extensions/libxt_recent.c
+++ b/extensions/libxt_recent.c
@@ -104,7 +104,7 @@ static void recent_help(void)
" --rsource Match/Save the source address of each packet in the recent list table (default).\n"
" --rdest Match/Save the destination address of each packet in the recent list table.\n"
" --mask netmask Netmask that will be applied to this recent list.\n"
-"xt_recent by: Stephen Frost <sfrost@snowman.net>. http://snowman.net/projects/ipt_recent/\n");
+"xt_recent by: Stephen Frost <sfrost@snowman.net>.\n");
}
enum {
diff --git a/extensions/libxt_recent.man b/extensions/libxt_recent.man
index d9bd5d2a..419be257 100644
--- a/extensions/libxt_recent.man
+++ b/extensions/libxt_recent.man
@@ -73,9 +73,6 @@ iptables \-A FORWARD \-m recent \-\-name badguy \-\-rcheck \-\-seconds 60 \-j DR
.IP
iptables \-A FORWARD \-p tcp \-i eth0 \-\-dport 139 \-m recent \-\-name badguy \-\-set \-j DROP
.PP
-Steve's ipt_recent website (http://snowman.net/projects/ipt_recent/) also has
-some examples of usage.
-.PP
\fB/proc/net/xt_recent/*\fP are the current lists of addresses and information
about each entry of each list.
.PP
diff --git a/extensions/libxt_recent.t b/extensions/libxt_recent.t
new file mode 100644
index 00000000..9a83918e
--- /dev/null
+++ b/extensions/libxt_recent.t
@@ -0,0 +1,11 @@
+:INPUT,FORWARD,OUTPUT
+-m recent --set;=;OK
+-m recent --rcheck --hitcount 8 --name foo --mask 255.255.255.255 --rsource;=;OK
+-m recent --rcheck --hitcount 12 --name foo --mask 255.255.255.255 --rsource;=;OK
+-m recent --update --rttl;=;OK
+-m recent --set --rttl;;FAIL
+-m recent --rcheck --hitcount 999 --name foo --mask 255.255.255.255 --rsource;;FAIL
+# nonsensical, but all should load successfully:
+-m recent --rcheck --hitcount 3 --name foo --mask 255.255.255.255 --rsource -m recent --rcheck --hitcount 4 --name foo --mask 255.255.255.255 --rsource;=;OK
+-m recent --rcheck --hitcount 4 --name foo --mask 255.255.255.255 --rsource -m recent --rcheck --hitcount 4 --name foo --mask 255.255.255.255 --rsource;=;OK
+-m recent --rcheck --hitcount 8 --name foo --mask 255.255.255.255 --rsource -m recent --rcheck --hitcount 12 --name foo --mask 255.255.255.255 --rsource;=;OK
diff --git a/extensions/libxt_rpfilter.man b/extensions/libxt_rpfilter.man
index f7f56d23..a3c0fcdc 100644
--- a/extensions/libxt_rpfilter.man
+++ b/extensions/libxt_rpfilter.man
@@ -8,7 +8,7 @@ Also, packets arriving via the loopback interface are always permitted.
This match can only be used in the PREROUTING chain of the raw or mangle table.
.TP
\fB\-\-loose\fP
-Used to specifiy that the reverse path filter test should match
+Used to specify that the reverse path filter test should match
even if the selected output device is not the expected one.
.TP
\fB\-\-validmark\fP
diff --git a/extensions/libxt_rpfilter.t b/extensions/libxt_rpfilter.t
new file mode 100644
index 00000000..390268f3
--- /dev/null
+++ b/extensions/libxt_rpfilter.t
@@ -0,0 +1,4 @@
+:PREROUTING
+*mangle
+-m rpfilter;=;OK
+-m rpfilter --loose --validmark --accept-local --invert;=;OK
diff --git a/extensions/libxt_sctp.c b/extensions/libxt_sctp.c
index 56a4cdf2..df1936be 100644
--- a/extensions/libxt_sctp.c
+++ b/extensions/libxt_sctp.c
@@ -485,6 +485,44 @@ static void sctp_save(const void *ip, const struct xt_entry_match *match)
}
}
+static int sctp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_sctp_info *einfo =
+ (const struct xt_sctp_info *)params->match->data;
+ char *space = "";
+
+ if (!einfo->flags)
+ return 0;
+
+ xt_xlate_add(xl, "sctp ");
+
+ if (einfo->flags & XT_SCTP_SRC_PORTS) {
+ if (einfo->spts[0] != einfo->spts[1])
+ xt_xlate_add(xl, "sport%s %u-%u",
+ einfo->invflags & XT_SCTP_SRC_PORTS ? " !=" : "",
+ einfo->spts[0], einfo->spts[1]);
+ else
+ xt_xlate_add(xl, "sport%s %u",
+ einfo->invflags & XT_SCTP_SRC_PORTS ? " !=" : "",
+ einfo->spts[0]);
+ space = " ";
+ }
+
+ if (einfo->flags & XT_SCTP_DEST_PORTS) {
+ if (einfo->dpts[0] != einfo->dpts[1])
+ xt_xlate_add(xl, "%sdport%s %u-%u", space,
+ einfo->invflags & XT_SCTP_DEST_PORTS ? " !=" : "",
+ einfo->dpts[0], einfo->dpts[1]);
+ else
+ xt_xlate_add(xl, "%sdport%s %u", space,
+ einfo->invflags & XT_SCTP_DEST_PORTS ? " !=" : "",
+ einfo->dpts[0]);
+ }
+
+ return 1;
+}
+
static struct xtables_match sctp_match = {
.name = "sctp",
.family = NFPROTO_UNSPEC,
@@ -497,6 +535,7 @@ static struct xtables_match sctp_match = {
.print = sctp_print,
.save = sctp_save,
.extra_opts = sctp_opts,
+ .xlate = sctp_xlate,
};
void _init(void)
diff --git a/extensions/libxt_sctp.t b/extensions/libxt_sctp.t
new file mode 100644
index 00000000..2f75e2a6
--- /dev/null
+++ b/extensions/libxt_sctp.t
@@ -0,0 +1,32 @@
+:INPUT,FORWARD,OUTPUT
+-p sctp -m sctp --sport 1;=;OK
+-p sctp -m sctp --sport 65535;=;OK
+-p sctp -m sctp --sport 1:65535;=;OK
+-p sctp -m sctp --sport -1;;FAIL
+-p sctp -m sctp --sport 65536;;FAIL
+-p sctp -m sctp --dport 1;=;OK
+-p sctp -m sctp --dport 1:65535;=;OK
+-p sctp -m sctp --dport 65535;=;OK
+-p sctp -m sctp --dport -1;;FAIL
+-p sctp -m sctp --dport 65536;;FAIL
+-p sctp -m sctp --chunk-types all DATA;=;OK
+-p sctp -m sctp --chunk-types all INIT;=;OK
+-p sctp -m sctp --chunk-types all INIT_ACK;=;OK
+-p sctp -m sctp --chunk-types all SACK;=;OK
+-p sctp -m sctp --chunk-types all HEARTBEAT;=;OK
+-p sctp -m sctp --chunk-types all HEARTBEAT_ACK;=;OK
+-p sctp -m sctp --chunk-types all ABORT;=;OK
+-p sctp -m sctp --chunk-types all SHUTDOWN;=;OK
+-p sctp -m sctp --chunk-types all SHUTDOWN_ACK;=;OK
+-p sctp -m sctp --chunk-types all ERROR;=;OK
+-p sctp -m sctp --chunk-types all COOKIE_ECHO;=;OK
+-p sctp -m sctp --chunk-types all COOKIE_ACK;=;OK
+-p sctp -m sctp --chunk-types all ECN_ECNE;=;OK
+-p sctp -m sctp --chunk-types all ECN_CWR;=;OK
+# ERROR: iptables-save segfaults: iptables -A INPUT -p sctp -m sctp --chunk-types all ASCONF
+# -p sctp -m sctp --chunk-types all ASCONF;=;OK
+# ERROR: iptables-save segfaults: iptables -A INPUT -p sctp -m sctp --chunk-types all ASCONF_ACK
+# -p sctp -m sctp --chunk-types all ASCONF_ACK;=;OK
+# ERROR: iptables-save segfaults: iptables -A INPUT -p sctp -m sctp --chunk-types all FORWARD_TSN
+# -p sctp -m sctp --chunk-types all FORWARD_TSN;=;OK
+-p sctp -m sctp --chunk-types all SHUTDOWN_COMPLETE;=;OK
diff --git a/extensions/libxt_set.c b/extensions/libxt_set.c
index 2cb9e78a..679c04c7 100644
--- a/extensions/libxt_set.c
+++ b/extensions/libxt_set.c
@@ -52,7 +52,7 @@ static int
set_parse_v0(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
- struct xt_set_info_match_v0 *myinfo =
+ struct xt_set_info_match_v0 *myinfo =
(struct xt_set_info_match_v0 *) (*match)->data;
struct xt_set_info_v0 *info = &myinfo->match_set;
@@ -82,7 +82,7 @@ set_parse_v0(int c, char **argv, int invert, unsigned int *flags,
parse_dirs_v0(argv[optind], info);
DEBUGP("parse: set index %u\n", info->index);
optind++;
-
+
*flags = 1;
break;
}
@@ -132,7 +132,7 @@ static int
set_parse_v1(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
- struct xt_set_info_match_v1 *myinfo =
+ struct xt_set_info_match_v1 *myinfo =
(struct xt_set_info_match_v1 *) (*match)->data;
struct xt_set_info *info = &myinfo->match_set;
@@ -162,7 +162,7 @@ set_parse_v1(int c, char **argv, int invert, unsigned int *flags,
parse_dirs(argv[optind], info);
DEBUGP("parse: set index %u\n", info->index);
optind++;
-
+
*flags = 1;
break;
}
@@ -227,7 +227,7 @@ static int
set_parse_v2(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
- struct xt_set_info_match_v1 *myinfo =
+ struct xt_set_info_match_v1 *myinfo =
(struct xt_set_info_match_v1 *) (*match)->data;
struct xt_set_info *info = &myinfo->match_set;
@@ -260,7 +260,7 @@ set_parse_v2(int c, char **argv, int invert, unsigned int *flags,
parse_dirs(argv[optind], info);
DEBUGP("parse: set index %u\n", info->index);
optind++;
-
+
*flags = 1;
break;
}
@@ -334,7 +334,7 @@ static int
set_parse_v3(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
- struct xt_set_info_match_v3 *info =
+ struct xt_set_info_match_v3 *info =
(struct xt_set_info_match_v3 *) (*match)->data;
switch (c) {
@@ -437,7 +437,7 @@ set_parse_v3(int c, char **argv, int invert, unsigned int *flags,
parse_dirs(argv[optind], &info->match_set);
DEBUGP("parse: set index %u\n", info->match_set.index);
optind++;
-
+
*flags = 1;
break;
}
@@ -446,7 +446,7 @@ set_parse_v3(int c, char **argv, int invert, unsigned int *flags,
}
static void
-set_printv3_counter(const struct ip_set_counter_match *c, const char *name,
+set_printv3_counter(const struct ip_set_counter_match0 *c, const char *name,
const char *sep)
{
switch (c->op) {
@@ -497,6 +497,174 @@ set_save_v3(const void *ip, const struct xt_entry_match *match)
set_print_v3_matchinfo(info, "--match-set", "--");
}
+/* Revision 4 */
+static int
+set_parse_v4(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_match **match)
+{
+ struct xt_set_info_match_v4 *info =
+ (struct xt_set_info_match_v4 *) (*match)->data;
+
+ switch (c) {
+ case 'a':
+ if (invert)
+ info->flags |= IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE;
+ break;
+ case '0':
+ if (info->bytes.op != IPSET_COUNTER_NONE)
+ xtables_error(PARAMETER_PROBLEM,
+ "only one of the --bytes-[eq|lt|gt]"
+ " is allowed\n");
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "--bytes-gt option cannot be inverted\n");
+ info->bytes.op = IPSET_COUNTER_GT;
+ info->bytes.value = parse_counter(optarg);
+ break;
+ case '9':
+ if (info->bytes.op != IPSET_COUNTER_NONE)
+ xtables_error(PARAMETER_PROBLEM,
+ "only one of the --bytes-[eq|lt|gt]"
+ " is allowed\n");
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "--bytes-lt option cannot be inverted\n");
+ info->bytes.op = IPSET_COUNTER_LT;
+ info->bytes.value = parse_counter(optarg);
+ break;
+ case '8':
+ if (info->bytes.op != IPSET_COUNTER_NONE)
+ xtables_error(PARAMETER_PROBLEM,
+ "only one of the --bytes-[eq|lt|gt]"
+ " is allowed\n");
+ info->bytes.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
+ info->bytes.value = parse_counter(optarg);
+ break;
+ case '7':
+ if (info->packets.op != IPSET_COUNTER_NONE)
+ xtables_error(PARAMETER_PROBLEM,
+ "only one of the --packets-[eq|lt|gt]"
+ " is allowed\n");
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "--packets-gt option cannot be inverted\n");
+ info->packets.op = IPSET_COUNTER_GT;
+ info->packets.value = parse_counter(optarg);
+ break;
+ case '6':
+ if (info->packets.op != IPSET_COUNTER_NONE)
+ xtables_error(PARAMETER_PROBLEM,
+ "only one of the --packets-[eq|lt|gt]"
+ " is allowed\n");
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "--packets-lt option cannot be inverted\n");
+ info->packets.op = IPSET_COUNTER_LT;
+ info->packets.value = parse_counter(optarg);
+ break;
+ case '5':
+ if (info->packets.op != IPSET_COUNTER_NONE)
+ xtables_error(PARAMETER_PROBLEM,
+ "only one of the --packets-[eq|lt|gt]"
+ " is allowed\n");
+ info->packets.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
+ info->packets.value = parse_counter(optarg);
+ break;
+ case '4':
+ if (invert)
+ info->flags |= IPSET_FLAG_SKIP_COUNTER_UPDATE;
+ break;
+ case '3':
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "--return-nomatch flag cannot be inverted\n");
+ info->flags |= IPSET_FLAG_RETURN_NOMATCH;
+ break;
+ case '2':
+ fprintf(stderr,
+ "--set option deprecated, please use --match-set\n");
+ case '1': /* --match-set <set> <flag>[,<flag> */
+ if (info->match_set.dim)
+ xtables_error(PARAMETER_PROBLEM,
+ "--match-set can be specified only once");
+ if (invert)
+ info->match_set.flags |= IPSET_INV_MATCH;
+
+ if (!argv[optind]
+ || argv[optind][0] == '-'
+ || argv[optind][0] == '!')
+ xtables_error(PARAMETER_PROBLEM,
+ "--match-set requires two args.");
+
+ if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "setname `%s' too long, max %d characters.",
+ optarg, IPSET_MAXNAMELEN - 1);
+
+ get_set_byname(optarg, &info->match_set);
+ parse_dirs(argv[optind], &info->match_set);
+ DEBUGP("parse: set index %u\n", info->match_set.index);
+ optind++;
+
+ *flags = 1;
+ break;
+ }
+
+ return 1;
+}
+
+static void
+set_printv4_counter(const struct ip_set_counter_match *c, const char *name,
+ const char *sep)
+{
+ switch (c->op) {
+ case IPSET_COUNTER_EQ:
+ printf(" %s%s-eq %llu", sep, name, c->value);
+ break;
+ case IPSET_COUNTER_NE:
+ printf(" ! %s%s-eq %llu", sep, name, c->value);
+ break;
+ case IPSET_COUNTER_LT:
+ printf(" %s%s-lt %llu", sep, name, c->value);
+ break;
+ case IPSET_COUNTER_GT:
+ printf(" %s%s-gt %llu", sep, name, c->value);
+ break;
+ }
+}
+
+static void
+set_print_v4_matchinfo(const struct xt_set_info_match_v4 *info,
+ const char *opt, const char *sep)
+{
+ print_match(opt, &info->match_set);
+ if (info->flags & IPSET_FLAG_RETURN_NOMATCH)
+ printf(" %sreturn-nomatch", sep);
+ if ((info->flags & IPSET_FLAG_SKIP_COUNTER_UPDATE))
+ printf(" ! %supdate-counters", sep);
+ if ((info->flags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE))
+ printf(" ! %supdate-subcounters", sep);
+ set_printv4_counter(&info->packets, "packets", sep);
+ set_printv4_counter(&info->bytes, "bytes", sep);
+}
+
+/* Prints out the matchinfo. */
+static void
+set_print_v4(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+ const struct xt_set_info_match_v4 *info = (const void *)match->data;
+
+ set_print_v4_matchinfo(info, "match-set", "");
+}
+
+static void
+set_save_v4(const void *ip, const struct xt_entry_match *match)
+{
+ const struct xt_set_info_match_v4 *info = (const void *)match->data;
+
+ set_print_v4_matchinfo(info, "--match-set", "--");
+}
+
static struct xtables_match set_mt_reg[] = {
{
.name = "set",
@@ -554,6 +722,20 @@ static struct xtables_match set_mt_reg[] = {
.save = set_save_v3,
.extra_opts = set_opts_v3,
},
+ {
+ .name = "set",
+ .revision = 4,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_UNSPEC,
+ .size = XT_ALIGN(sizeof(struct xt_set_info_match_v4)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_match_v4)),
+ .help = set_help_v3,
+ .parse = set_parse_v4,
+ .final_check = set_check_v0,
+ .print = set_print_v4,
+ .save = set_save_v4,
+ .extra_opts = set_opts_v3,
+ },
};
void _init(void)
diff --git a/extensions/libxt_set.h b/extensions/libxt_set.h
index 47c3f5b6..5a1bdcf7 100644
--- a/extensions/libxt_set.h
+++ b/extensions/libxt_set.h
@@ -6,6 +6,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
+#include "../iptables/xshared.h"
#ifdef DEBUG
#define DEBUGP(x, args...) fprintf(stderr, x , ## args)
@@ -71,13 +72,13 @@ get_set_byid(char *setname, ip_set_id_t idx)
}
static void
-get_set_byname(const char *setname, struct xt_set_info *info)
+get_set_byname_only(const char *setname, struct xt_set_info *info,
+ int sockfd, unsigned int version)
{
- struct ip_set_req_get_set req;
+ struct ip_set_req_get_set req = { .version = version };
socklen_t size = sizeof(struct ip_set_req_get_set);
- int res, sockfd;
+ int res;
- sockfd = get_version(&req.version);
req.op = IP_SET_OP_GET_BYNAME;
strncpy(req.set.name, setname, IPSET_MAXNAMELEN);
req.set.name[IPSET_MAXNAMELEN - 1] = '\0';
@@ -101,6 +102,49 @@ get_set_byname(const char *setname, struct xt_set_info *info)
}
static void
+get_set_byname(const char *setname, struct xt_set_info *info)
+{
+ struct ip_set_req_get_set_family req;
+ socklen_t size = sizeof(struct ip_set_req_get_set_family);
+ int res, sockfd, version;
+
+ sockfd = get_version(&req.version);
+ version = req.version;
+ req.op = IP_SET_OP_GET_FNAME;
+ strncpy(req.set.name, setname, IPSET_MAXNAMELEN);
+ req.set.name[IPSET_MAXNAMELEN - 1] = '\0';
+ res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
+
+ if (res != 0 && errno == EBADMSG)
+ /* Backward compatibility */
+ return get_set_byname_only(setname, info, sockfd, version);
+
+ close(sockfd);
+ if (res != 0)
+ xtables_error(OTHER_PROBLEM,
+ "Problem when communicating with ipset, errno=%d.\n",
+ errno);
+ if (size != sizeof(struct ip_set_req_get_set_family))
+ xtables_error(OTHER_PROBLEM,
+ "Incorrect return size from kernel during ipset lookup, "
+ "(want %zu, got %zu)\n",
+ sizeof(struct ip_set_req_get_set_family),
+ (size_t)size);
+ if (req.set.index == IPSET_INVALID_ID)
+ xtables_error(PARAMETER_PROBLEM,
+ "Set %s doesn't exist.\n", setname);
+ if (!(req.family == afinfo->family ||
+ req.family == NFPROTO_UNSPEC))
+ xtables_error(PARAMETER_PROBLEM,
+ "The protocol family of set %s is %s, "
+ "which is not applicable.\n",
+ setname,
+ req.family == NFPROTO_IPV4 ? "IPv4" : "IPv6");
+
+ info->index = req.set.index;
+}
+
+static void
parse_dirs_v0(const char *opt_arg, struct xt_set_info_v0 *info)
{
char *saved = strdup(opt_arg);
diff --git a/extensions/libxt_set.man b/extensions/libxt_set.man
index 7012ef2e..dbc1586b 100644
--- a/extensions/libxt_set.man
+++ b/extensions/libxt_set.man
@@ -43,7 +43,7 @@ packet counter of the element is less than the given value as well.
If the packet is matched an element in the set, match only if the
packet counter of the element is greater than the given value as well.
.TP
-[\fB!\fP] \fB\-bytes\-eq\fP \fIvalue\fP
+[\fB!\fP] \fB\-\-bytes\-eq\fP \fIvalue\fP
If the packet is matched an element in the set, match only if the
byte counter of the element matches the given value too.
.TP
diff --git a/extensions/libxt_set.t b/extensions/libxt_set.t
new file mode 100644
index 00000000..dd9e9f17
--- /dev/null
+++ b/extensions/libxt_set.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD,OUTPUT
+-m set --match-set foo;;FAIL
+# fails: foo does not exist
+-m set --match-set foo src,dst;;FAIL
diff --git a/extensions/libxt_socket.man b/extensions/libxt_socket.man
index 41e8d674..f809df69 100644
--- a/extensions/libxt_socket.man
+++ b/extensions/libxt_socket.man
@@ -1,5 +1,36 @@
-This matches if an open socket can be found by doing a socket lookup on the
-packet.
+This matches if an open TCP/UDP socket can be found by doing a socket lookup on the
+packet. It matches if there is an established or non\-zero bound listening
+socket (possibly with a non\-local address). The lookup is performed using
+the \fBpacket\fP tuple of TCP/UDP packets, or the original TCP/UDP header
+\fBembedded\fP in an ICMP/ICPMv6 error packet.
.TP
\fB\-\-transparent\fP
Ignore non-transparent sockets.
+.TP
+\fB\-\-nowildcard\fP
+Do not ignore sockets bound to 'any' address.
+The socket match won't accept zero\-bound listeners by default, since
+then local services could intercept traffic that would otherwise be forwarded.
+This option therefore has security implications when used to match traffic being
+forwarded to redirect such packets to local machine with policy routing.
+When using the socket match to implement fully transparent
+proxies bound to non\-local addresses it is recommended to use the \-\-transparent
+option instead.
+.PP
+Example (assuming packets with mark 1 are delivered locally):
+.IP
+\-t mangle \-A PREROUTING \-m socket \-\-transparent \-j MARK \-\-set\-mark 1
+.TP
+\fB\-\-restore\-skmark\fP
+Set the packet mark to the matching socket's mark. Can be combined with the
+\fB\-\-transparent\fP and \fB\-\-nowildcard\fP options to restrict the sockets
+to be matched when restoring the packet mark.
+.PP
+Example: An application opens 2 transparent (\fBIP_TRANSPARENT\fP) sockets and
+sets a mark on them with \fBSO_MARK\fP socket option. We can filter matching packets:
+.IP
+\-t mangle \-I PREROUTING \-m socket \-\-transparent \-\-restore-skmark \-j action
+.IP
+\-t mangle \-A action \-m mark \-\-mark 10 \-j action2
+.IP
+\-t mangle \-A action \-m mark \-\-mark 11 \-j action3
diff --git a/extensions/libxt_socket.t b/extensions/libxt_socket.t
new file mode 100644
index 00000000..fe4eb3e4
--- /dev/null
+++ b/extensions/libxt_socket.t
@@ -0,0 +1,8 @@
+:PREROUTING,INPUT
+*mangle
+-m socket;=;OK
+-m socket --transparent --nowildcard;=;OK
+-m socket --transparent --nowildcard --restore-skmark;=;OK
+-m socket --transparent --restore-skmark;=;OK
+-m socket --nowildcard --restore-skmark;=;OK
+-m socket --restore-skmark;=;OK
diff --git a/extensions/libxt_standard.t b/extensions/libxt_standard.t
new file mode 100644
index 00000000..923569c3
--- /dev/null
+++ b/extensions/libxt_standard.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD,OUTPUT
+-j DROP;=;OK
+-j ACCEPT;=;OK
+-j RETURN;=;OK
diff --git a/extensions/libxt_state.t b/extensions/libxt_state.t
new file mode 100644
index 00000000..8e4bce3f
--- /dev/null
+++ b/extensions/libxt_state.t
@@ -0,0 +1,6 @@
+:INPUT,FORWARD,OUTPUT
+-m state --state INVALID;=;OK
+-m state --state NEW,RELATED;=;OK
+-m state --state UNTRACKED;=;OK
+-m state wrong;;FAIL
+-m state;;FAIL
diff --git a/extensions/libxt_statistic.c b/extensions/libxt_statistic.c
index b6ae5f5c..4f3341a3 100644
--- a/extensions/libxt_statistic.c
+++ b/extensions/libxt_statistic.c
@@ -133,6 +133,26 @@ static void statistic_save(const void *ip, const struct xt_entry_match *match)
print_match(info, "--");
}
+static int statistic_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_statistic_info *info =
+ (struct xt_statistic_info *)params->match->data;
+
+ switch (info->mode) {
+ case XT_STATISTIC_MODE_RANDOM:
+ return 0;
+ case XT_STATISTIC_MODE_NTH:
+ xt_xlate_add(xl, "numgen inc mod %u %s%u",
+ info->u.nth.every + 1,
+ info->flags & XT_STATISTIC_INVERT ? "!= " : "",
+ info->u.nth.packet);
+ break;
+ }
+
+ return 1;
+}
+
static struct xtables_match statistic_match = {
.family = NFPROTO_UNSPEC,
.name = "statistic",
@@ -145,6 +165,7 @@ static struct xtables_match statistic_match = {
.print = statistic_print,
.save = statistic_save,
.x6_options = statistic_opts,
+ .xlate = statistic_xlate,
};
void _init(void)
diff --git a/extensions/libxt_statistic.t b/extensions/libxt_statistic.t
new file mode 100644
index 00000000..bb6673da
--- /dev/null
+++ b/extensions/libxt_statistic.t
@@ -0,0 +1,8 @@
+:INPUT,FORWARD,OUTPUT
+-m statistic;;FAIL
+-m statistic --mode random ! --probability 0.50000000000;=;OK
+-m statistic --mode random ! --probability 1.1;;FAIL
+-m statistic --probability 1;;FAIL
+-m statistic --mode nth ! --every 5 --packet 2;=;OK
+-m statistic --mode nth ! --every 5;;FAIL
+-m statistic --mode nth ! --every 5 --packet 5;;FAIL
diff --git a/extensions/libxt_string.man b/extensions/libxt_string.man
index b6b271d1..54c03a3a 100644
--- a/extensions/libxt_string.man
+++ b/extensions/libxt_string.man
@@ -16,3 +16,16 @@ Matches the given pattern.
.TP
[\fB!\fP] \fB\-\-hex\-string\fP \fIpattern\fP
Matches the given pattern in hex notation.
+.TP
+\fB\-\-icase\fP
+Ignore case when searching.
+.TP
+Examples:
+.IP
+# The string pattern can be used for simple text characters.
+.br
+iptables \-A INPUT \-p tcp \-\-dport 80 \-m string \-\-algo bm \-\-string 'GET /index.html' \-j LOG
+.IP
+# The hex string pattern can be used for non-printable characters, like |0D 0A| or |0D0A|.
+.br
+iptables \-p udp \-\-dport 53 \-m string \-\-algo bm \-\-from 40 \-\-to 57 \-\-hex\-string '|03|www|09|netfilter|03|org|00|'
diff --git a/extensions/libxt_string.t b/extensions/libxt_string.t
new file mode 100644
index 00000000..d68f099d
--- /dev/null
+++ b/extensions/libxt_string.t
@@ -0,0 +1,18 @@
+:INPUT,FORWARD,OUTPUT
+# ERROR: cannot find: iptables -I INPUT -m string --algo bm --string "test"
+# -m string --algo bm --string "test";=;OK
+# ERROR: cannot find: iptables -I INPUT -m string --algo kmp --string "test")
+# -m string --algo kmp --string "test";=;OK
+# ERROR: cannot find: iptables -I INPUT -m string --algo kmp ! --string "test"
+# -m string --algo kmp ! --string "test";=;OK
+# cannot find: iptables -I INPUT -m string --algo bm --string "xxxxxxxxxxx" ....]
+# -m string --algo bm --string "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";=;OK
+# ERROR: cannot load: iptables -A INPUT -m string --algo bm --string "xxxx"
+# -m string --algo bm --string "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";=;OK
+# ERROR: cannot load: iptables -A INPUT -m string --algo bm --hexstring "|0a0a0a0a|"
+# -m string --algo bm --hexstring "|0a0a0a0a|";=;OK
+# ERROR: cannot find: iptables -I INPUT -m string --algo bm --from 0 --to 65535 --string "test"
+# -m string --algo bm --from 0 --to 65535 --string "test";=;OK
+-m string --algo wrong;;FAIL
+-m string --algo bm;;FAIL
+-m string;;FAIL
diff --git a/extensions/libxt_tcp.c b/extensions/libxt_tcp.c
index bbdec454..58f3c0a0 100644
--- a/extensions/libxt_tcp.c
+++ b/extensions/libxt_tcp.c
@@ -362,6 +362,89 @@ static void tcp_save(const void *ip, const struct xt_entry_match *match)
}
}
+static const struct tcp_flag_names tcp_flag_names_xlate[] = {
+ { "fin", 0x01 },
+ { "syn", 0x02 },
+ { "rst", 0x04 },
+ { "psh", 0x08 },
+ { "ack", 0x10 },
+ { "urg", 0x20 },
+};
+
+static void print_tcp_xlate(struct xt_xlate *xl, uint8_t flags)
+{
+ int have_flag = 0;
+
+ while (flags) {
+ unsigned int i;
+
+ for (i = 0; (flags & tcp_flag_names_xlate[i].flag) == 0; i++);
+
+ if (have_flag)
+ xt_xlate_add(xl, "|");
+
+ xt_xlate_add(xl, "%s", tcp_flag_names_xlate[i].name);
+ have_flag = 1;
+
+ flags &= ~tcp_flag_names_xlate[i].flag;
+ }
+
+ if (!have_flag)
+ xt_xlate_add(xl, "0x0");
+}
+
+static int tcp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_tcp *tcpinfo =
+ (const struct xt_tcp *)params->match->data;
+ char *space= "";
+
+ if (tcpinfo->spts[0] != 0 || tcpinfo->spts[1] != 0xffff) {
+ if (tcpinfo->spts[0] != tcpinfo->spts[1]) {
+ xt_xlate_add(xl, "tcp sport %s%u-%u",
+ tcpinfo->invflags & XT_TCP_INV_SRCPT ?
+ "!= " : "",
+ tcpinfo->spts[0], tcpinfo->spts[1]);
+ } else {
+ xt_xlate_add(xl, "tcp sport %s%u",
+ tcpinfo->invflags & XT_TCP_INV_SRCPT ?
+ "!= " : "",
+ tcpinfo->spts[0]);
+ }
+ space = " ";
+ }
+
+ if (tcpinfo->dpts[0] != 0 || tcpinfo->dpts[1] != 0xffff) {
+ if (tcpinfo->dpts[0] != tcpinfo->dpts[1]) {
+ xt_xlate_add(xl, "%stcp dport %s%u-%u", space,
+ tcpinfo->invflags & XT_TCP_INV_DSTPT ?
+ "!= " : "",
+ tcpinfo->dpts[0], tcpinfo->dpts[1]);
+ } else {
+ xt_xlate_add(xl, "%stcp dport %s%u", space,
+ tcpinfo->invflags & XT_TCP_INV_DSTPT ?
+ "!= " : "",
+ tcpinfo->dpts[0]);
+ }
+ space = " ";
+ }
+
+ /* XXX not yet implemented */
+ if (tcpinfo->option || (tcpinfo->invflags & XT_TCP_INV_OPTION))
+ return 0;
+
+ if (tcpinfo->flg_mask || (tcpinfo->invflags & XT_TCP_INV_FLAGS)) {
+ xt_xlate_add(xl, "%stcp flags & (", space);
+ print_tcp_xlate(xl, tcpinfo->flg_mask);
+ xt_xlate_add(xl, ") %s ",
+ tcpinfo->invflags & XT_TCP_INV_FLAGS ? "!=": "==");
+ print_tcp_xlate(xl, tcpinfo->flg_cmp);
+ }
+
+ return 1;
+}
+
static struct xtables_match tcp_match = {
.family = NFPROTO_UNSPEC,
.name = "tcp",
@@ -374,6 +457,7 @@ static struct xtables_match tcp_match = {
.print = tcp_print,
.save = tcp_save,
.extra_opts = tcp_opts,
+ .xlate = tcp_xlate,
};
void
diff --git a/extensions/libxt_tcp.man b/extensions/libxt_tcp.man
index 7a16118b..80194617 100644
--- a/extensions/libxt_tcp.man
+++ b/extensions/libxt_tcp.man
@@ -7,7 +7,6 @@ name or a port number. An inclusive range can also be specified,
using the format \fIfirst\fP\fB:\fP\fIlast\fP.
If the first port is omitted, "0" is assumed; if the last is omitted,
"65535" is assumed.
-If the first port is greater than the second one they will be swapped.
The flag
\fB\-\-sport\fP
is a convenient alias for this option.
diff --git a/extensions/libxt_tcp.t b/extensions/libxt_tcp.t
new file mode 100644
index 00000000..b0e8006e
--- /dev/null
+++ b/extensions/libxt_tcp.t
@@ -0,0 +1,26 @@
+:INPUT,FORWARD,OUTPUT
+-p tcp -m tcp --sport 1;=;OK
+-p tcp -m tcp --sport 65535;=;OK
+-p tcp -m tcp --dport 1;=;OK
+-p tcp -m tcp --dport 65535;=;OK
+-p tcp -m tcp --sport 1:1023;=;OK
+-p tcp -m tcp --sport 1024:65535;=;OK
+-p tcp -m tcp --sport 1024:;-p tcp -m tcp --sport 1024:65535;OK
+-p tcp -m tcp ! --sport 1;=;OK
+-p tcp -m tcp ! --sport 65535;=;OK
+-p tcp -m tcp ! --dport 1;=;OK
+-p tcp -m tcp ! --dport 65535;=;OK
+-p tcp -m tcp --sport 1 --dport 65535;=;OK
+-p tcp -m tcp --sport 65535 --dport 1;=;OK
+-p tcp -m tcp ! --sport 1 --dport 65535;=;OK
+-p tcp -m tcp ! --sport 65535 --dport 1;=;OK
+-p tcp -m tcp --sport 65536;;FAIL
+-p tcp -m tcp --sport -1;;FAIL
+-p tcp -m tcp --dport -1;;FAIL
+-p tcp -m tcp --syn;-p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN;OK
+-p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN;=;OK
+-p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG SYN;=;OK
+-p tcp -m tcp ! --tcp-flags FIN,SYN,RST,PSH,ACK,URG SYN;=;OK
+-p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG RST;=;OK
+# should we accept this below?
+-p tcp -m tcp;=;OK
diff --git a/extensions/libxt_tcpmss.t b/extensions/libxt_tcpmss.t
new file mode 100644
index 00000000..3181e49d
--- /dev/null
+++ b/extensions/libxt_tcpmss.t
@@ -0,0 +1,5 @@
+:INPUT,FORWARD,OUTPUT
+-m tcpmss --mss 42;;FAIL
+-p tcp -m tcpmss --mss 42;=;OK
+-p tcp -m tcpmss --mss 42:12345;=;OK
+-p tcp -m tcpmss --mss 42:65536;;FAIL
diff --git a/extensions/libxt_time.t b/extensions/libxt_time.t
new file mode 100644
index 00000000..673af09b
--- /dev/null
+++ b/extensions/libxt_time.t
@@ -0,0 +1,4 @@
+:INPUT,FORWARD,OUTPUT
+-m time --timestart 01:02:03 --timestop 04:05:06 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05 --kerneltz;=;OK
+-m time --timestart 01:02:03 --timestop 04:05:06 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05;=;OK
+-m time --timestart 02:00:00 --timestop 03:00:00 --datestart 1970-01-01T02:00:00 --datestop 1970-01-01T03:00:00;=;OK
diff --git a/extensions/libxt_tos.t b/extensions/libxt_tos.t
new file mode 100644
index 00000000..ccbe8009
--- /dev/null
+++ b/extensions/libxt_tos.t
@@ -0,0 +1,13 @@
+:INPUT,FORWARD,OUTPUT
+-m tos --tos Minimize-Delay;-m tos --tos 0x10/0x3f;OK
+-m tos --tos Maximize-Throughput;-m tos --tos 0x08/0x3f;OK
+-m tos --tos Maximize-Reliability;-m tos --tos 0x04/0x3f;OK
+-m tos --tos Minimize-Cost;-m tos --tos 0x02/0x3f;OK
+-m tos --tos Normal-Service;-m tos --tos 0x00/0x3f;OK
+-m tos --tos 0xff;=;OK
+-m tos ! --tos 0xff;=;OK
+-m tos --tos 0x00;=;OK
+-m tos --tos 0x0f;=;OK
+-m tos --tos 0x0f/0x0f;=;OK
+-m tos --tos wrong;;FAIL
+-m tos;;FAIL
diff --git a/extensions/libxt_u32.t b/extensions/libxt_u32.t
new file mode 100644
index 00000000..0d9be47a
--- /dev/null
+++ b/extensions/libxt_u32.t
@@ -0,0 +1,2 @@
+:INPUT,FORWARD,OUTPUT
+-m u32 --u32 "0x0=0x0&&0x0=0x1";=;OK
diff --git a/extensions/libxt_udp.c b/extensions/libxt_udp.c
index b9f39ee4..0c7a4bc2 100644
--- a/extensions/libxt_udp.c
+++ b/extensions/libxt_udp.c
@@ -152,6 +152,44 @@ static void udp_save(const void *ip, const struct xt_entry_match *match)
}
}
+static int udp_xlate(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params)
+{
+ const struct xt_udp *udpinfo = (struct xt_udp *)params->match->data;
+ char *space= "";
+
+ if (udpinfo->spts[0] != 0 || udpinfo->spts[1] != 0xFFFF) {
+ if (udpinfo->spts[0] != udpinfo->spts[1]) {
+ xt_xlate_add(xl,"udp sport %s%u-%u",
+ udpinfo->invflags & XT_UDP_INV_SRCPT ?
+ "!= ": "",
+ udpinfo->spts[0], udpinfo->spts[1]);
+ } else {
+ xt_xlate_add(xl, "udp sport %s%u",
+ udpinfo->invflags & XT_UDP_INV_SRCPT ?
+ "!= ": "",
+ udpinfo->spts[0]);
+ }
+ space = " ";
+ }
+
+ if (udpinfo->dpts[0] != 0 || udpinfo->dpts[1] != 0xFFFF) {
+ if (udpinfo->dpts[0] != udpinfo->dpts[1]) {
+ xt_xlate_add(xl,"%sudp dport %s%u-%u", space,
+ udpinfo->invflags & XT_UDP_INV_SRCPT ?
+ "!= ": "",
+ udpinfo->dpts[0], udpinfo->dpts[1]);
+ } else {
+ xt_xlate_add(xl,"%sudp dport %s%u", space,
+ udpinfo->invflags & XT_UDP_INV_SRCPT ?
+ "!= ": "",
+ udpinfo->dpts[0]);
+ }
+ }
+
+ return 1;
+}
+
static struct xtables_match udp_match = {
.family = NFPROTO_UNSPEC,
.name = "udp",
@@ -164,6 +202,7 @@ static struct xtables_match udp_match = {
.save = udp_save,
.x6_parse = udp_parse,
.x6_options = udp_opts,
+ .xlate = udp_xlate,
};
void
diff --git a/extensions/libxt_udp.t b/extensions/libxt_udp.t
new file mode 100644
index 00000000..1b4d3dd6
--- /dev/null
+++ b/extensions/libxt_udp.t
@@ -0,0 +1,22 @@
+:INPUT,OUTPUT,FORWARD
+-p udp -m udp --sport 1;=;OK
+-p udp -m udp --sport 65535;=;OK
+-p udp -m udp --dport 1;=;OK
+-p udp -m udp --dport 65535;=;OK
+-p udp -m udp --sport 1:1023;=;OK
+-p udp -m udp --sport 1024:65535;=;OK
+-p udp -m udp --sport 1024:;-p udp -m udp --sport 1024:65535;OK
+-p udp -m udp ! --sport 1;=;OK
+-p udp -m udp ! --sport 65535;=;OK
+-p udp -m udp ! --dport 1;=;OK
+-p udp -m udp ! --dport 65535;=;OK
+-p udp -m udp --sport 1 --dport 65535;=;OK
+-p udp -m udp --sport 65535 --dport 1;=;OK
+-p udp -m udp ! --sport 1 --dport 65535;=;OK
+-p udp -m udp ! --sport 65535 --dport 1;=;OK
+# ERRROR: should fail: iptables -A INPUT -p udp -m udp --sport 65536
+# -p udp -m udp --sport 65536;;FAIL
+-p udp -m udp --sport -1;;FAIL
+-p udp -m udp --dport -1;;FAIL
+# should we accept this below?
+-p udp -m udp;=;OK
diff --git a/include/ebtables/ethernetdb.h b/include/ebtables/ethernetdb.h
new file mode 100644
index 00000000..1683abe0
--- /dev/null
+++ b/include/ebtables/ethernetdb.h
@@ -0,0 +1,57 @@
+/*
+* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* All data returned by the network data base library are supplied in
+ host order and returned in network order (suitable for use in
+ system calls). */
+
+#ifndef _ETHERNETDB_H
+#define _ETHERNETDB_H 1
+
+#include <features.h>
+#include <netinet/in.h>
+#include <stdint.h>
+
+/* Absolute file name for network data base files. */
+#ifndef _PATH_ETHERTYPES
+#define _PATH_ETHERTYPES "/etc/ethertypes"
+#endif /* _PATH_ETHERTYPES */
+
+struct ethertypeent {
+ char *e_name; /* Official ethernet type name. */
+ char **e_aliases; /* Alias list. */
+ int e_ethertype; /* Ethernet type number. */
+};
+
+/* Open ethertype data base files and mark them as staying open even
+ after a later search if STAY_OPEN is non-zero. */
+extern void setethertypeent(int __stay_open);
+
+/* Close ethertype data base files and clear `stay open' flag. */
+extern void endethertypeent(void);
+
+/* Get next entry from ethertype data base file. Open data base if
+ necessary. */
+extern struct ethertypeent *getethertypeent(void);
+
+/* Return entry from ethertype data base for network with NAME. */
+extern struct ethertypeent *getethertypebyname(__const char *__name);
+
+/* Return entry from ethertype data base which number is PROTO. */
+extern struct ethertypeent *getethertypebynumber(int __ethertype);
+
+
+#endif /* ethernetdb.h */
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/iptables/internal.h b/include/iptables/internal.h
index 82b4c36c..3b9013ab 100644
--- a/include/iptables/internal.h
+++ b/include/iptables/internal.h
@@ -1,7 +1,7 @@
#ifndef IPTABLES_INTERNAL_H
#define IPTABLES_INTERNAL_H 1
-#define IPTABLES_VERSION "1.4.20"
+#define IPTABLES_VERSION "1.6.1"
/**
* Program's own name and version.
diff --git a/include/libiptc/ipt_kernel_headers.h b/include/libiptc/ipt_kernel_headers.h
index 60c79982..a5963e94 100644
--- a/include/libiptc/ipt_kernel_headers.h
+++ b/include/libiptc/ipt_kernel_headers.h
@@ -5,7 +5,6 @@
#include <limits.h>
-#if defined(__ANDROID__) || (defined(__GLIBC__) && __GLIBC__ == 2)
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
@@ -13,15 +12,4 @@
#include <netinet/udp.h>
#include <net/if.h>
#include <sys/types.h>
-#else /* libc5 */
-#include <sys/socket.h>
-#include <linux/ip.h>
-#include <linux/in.h>
-#include <linux/if.h>
-#include <linux/icmp.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/types.h>
-#include <linux/in6.h>
-#endif
#endif
diff --git a/include/linux/filter.h b/include/linux/filter.h
new file mode 100644
index 00000000..a9ae93c0
--- /dev/null
+++ b/include/linux/filter.h
@@ -0,0 +1,139 @@
+/*
+ * Linux Socket Filter Data Structures
+ */
+
+#ifndef __LINUX_FILTER_H__
+#define __LINUX_FILTER_H__
+
+
+#include <linux/types.h>
+
+
+/*
+ * Current version of the filter code architecture.
+ */
+#define BPF_MAJOR_VERSION 1
+#define BPF_MINOR_VERSION 1
+
+/*
+ * Try and keep these values and structures similar to BSD, especially
+ * the BPF code definitions which need to match so you can share filters
+ */
+
+struct sock_filter { /* Filter block */
+ __u16 code; /* Actual filter code */
+ __u8 jt; /* Jump true */
+ __u8 jf; /* Jump false */
+ __u32 k; /* Generic multiuse field */
+};
+
+struct sock_fprog { /* Required for SO_ATTACH_FILTER. */
+ unsigned short len; /* Number of filter blocks */
+ struct sock_filter *filter;
+};
+
+/*
+ * Instruction classes
+ */
+
+#define BPF_CLASS(code) ((code) & 0x07)
+#define BPF_LD 0x00
+#define BPF_LDX 0x01
+#define BPF_ST 0x02
+#define BPF_STX 0x03
+#define BPF_ALU 0x04
+#define BPF_JMP 0x05
+#define BPF_RET 0x06
+#define BPF_MISC 0x07
+
+/* ld/ldx fields */
+#define BPF_SIZE(code) ((code) & 0x18)
+#define BPF_W 0x00
+#define BPF_H 0x08
+#define BPF_B 0x10
+#define BPF_MODE(code) ((code) & 0xe0)
+#define BPF_IMM 0x00
+#define BPF_ABS 0x20
+#define BPF_IND 0x40
+#define BPF_MEM 0x60
+#define BPF_LEN 0x80
+#define BPF_MSH 0xa0
+
+/* alu/jmp fields */
+#define BPF_OP(code) ((code) & 0xf0)
+#define BPF_ADD 0x00
+#define BPF_SUB 0x10
+#define BPF_MUL 0x20
+#define BPF_DIV 0x30
+#define BPF_OR 0x40
+#define BPF_AND 0x50
+#define BPF_LSH 0x60
+#define BPF_RSH 0x70
+#define BPF_NEG 0x80
+#define BPF_MOD 0x90
+#define BPF_XOR 0xa0
+
+#define BPF_JA 0x00
+#define BPF_JEQ 0x10
+#define BPF_JGT 0x20
+#define BPF_JGE 0x30
+#define BPF_JSET 0x40
+#define BPF_SRC(code) ((code) & 0x08)
+#define BPF_K 0x00
+#define BPF_X 0x08
+
+/* ret - BPF_K and BPF_X also apply */
+#define BPF_RVAL(code) ((code) & 0x18)
+#define BPF_A 0x10
+
+/* misc */
+#define BPF_MISCOP(code) ((code) & 0xf8)
+#define BPF_TAX 0x00
+#define BPF_TXA 0x80
+
+#ifndef BPF_MAXINSNS
+#define BPF_MAXINSNS 4096
+#endif
+
+/*
+ * Macros for filter block array initializers.
+ */
+#ifndef BPF_STMT
+#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k }
+#endif
+#ifndef BPF_JUMP
+#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k }
+#endif
+
+/*
+ * Number of scratch memory words for: BPF_ST and BPF_STX
+ */
+#define BPF_MEMWORDS 16
+
+/* RATIONALE. Negative offsets are invalid in BPF.
+ We use them to reference ancillary data.
+ Unlike introduction new instructions, it does not break
+ existing compilers/optimizers.
+ */
+#define SKF_AD_OFF (-0x1000)
+#define SKF_AD_PROTOCOL 0
+#define SKF_AD_PKTTYPE 4
+#define SKF_AD_IFINDEX 8
+#define SKF_AD_NLATTR 12
+#define SKF_AD_NLATTR_NEST 16
+#define SKF_AD_MARK 20
+#define SKF_AD_QUEUE 24
+#define SKF_AD_HATYPE 28
+#define SKF_AD_RXHASH 32
+#define SKF_AD_CPU 36
+#define SKF_AD_ALU_XOR_X 40
+#define SKF_AD_VLAN_TAG 44
+#define SKF_AD_VLAN_TAG_PRESENT 48
+#define SKF_AD_PAY_OFFSET 52
+#define SKF_AD_RANDOM 56
+#define SKF_AD_MAX 60
+#define SKF_NET_OFF (-0x100000)
+#define SKF_LL_OFF (-0x200000)
+
+
+#endif /* __LINUX_FILTER_H__ */
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index eb9123e6..b69bbc72 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -110,6 +110,9 @@ enum {
IPSET_ATTR_IFACE,
IPSET_ATTR_BYTES,
IPSET_ATTR_PACKETS,
+ IPSET_ATTR_SKBMARK,
+ IPSET_ATTR_SKBPRIO,
+ IPSET_ATTR_SKBQUEUE,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
@@ -140,6 +143,7 @@ enum ipset_errno {
IPSET_ERR_IPADDR_IPV4,
IPSET_ERR_IPADDR_IPV6,
IPSET_ERR_COUNTER,
+ IPSET_ERR_SKBINFO,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 4352,
@@ -163,6 +167,12 @@ enum ipset_cmd_flags {
IPSET_FLAG_MATCH_COUNTERS = (1 << IPSET_FLAG_BIT_MATCH_COUNTERS),
IPSET_FLAG_BIT_RETURN_NOMATCH = 7,
IPSET_FLAG_RETURN_NOMATCH = (1 << IPSET_FLAG_BIT_RETURN_NOMATCH),
+ IPSET_FLAG_BIT_MAP_SKBMARK = 8,
+ IPSET_FLAG_MAP_SKBMARK = (1 << IPSET_FLAG_BIT_MAP_SKBMARK),
+ IPSET_FLAG_BIT_MAP_SKBPRIO = 9,
+ IPSET_FLAG_MAP_SKBPRIO = (1 << IPSET_FLAG_BIT_MAP_SKBPRIO),
+ IPSET_FLAG_BIT_MAP_SKBQUEUE = 10,
+ IPSET_FLAG_MAP_SKBQUEUE = (1 << IPSET_FLAG_BIT_MAP_SKBQUEUE),
IPSET_FLAG_CMD_MAX = 15,
};
@@ -226,11 +236,17 @@ enum {
IPSET_COUNTER_GT,
};
-struct ip_set_counter_match {
+/* Backward compatibility for set match v3 */
+struct ip_set_counter_match0 {
__u8 op;
__u64 value;
};
+struct ip_set_counter_match {
+ __aligned_u64 value;
+ __u8 op;
+};
+
/* Interface to iptables/ip6tables */
#define SO_IP_SET 83
@@ -250,6 +266,15 @@ struct ip_set_req_get_set {
#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */
/* Uses ip_set_req_get_set */
+#define IP_SET_OP_GET_FNAME 0x00000008 /* Get set index and family */
+struct ip_set_req_get_set_family {
+ unsigned int op;
+ unsigned int version;
+ unsigned int family;
+ union ip_set_name_index set;
+};
+
+
#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
struct ip_set_req_version {
unsigned int op;
diff --git a/include/linux/netfilter/nf_nat.h b/include/linux/netfilter/nf_nat.h
index bf0cc373..1ad36591 100644
--- a/include/linux/netfilter/nf_nat.h
+++ b/include/linux/netfilter/nf_nat.h
@@ -4,10 +4,14 @@
#include <linux/netfilter.h>
#include <linux/netfilter/nf_conntrack_tuple_common.h>
-#define NF_NAT_RANGE_MAP_IPS 1
-#define NF_NAT_RANGE_PROTO_SPECIFIED 2
-#define NF_NAT_RANGE_PROTO_RANDOM 4
-#define NF_NAT_RANGE_PERSISTENT 8
+#define NF_NAT_RANGE_MAP_IPS (1 << 0)
+#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1)
+#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2)
+#define NF_NAT_RANGE_PERSISTENT (1 << 3)
+#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
+
+#define NF_NAT_RANGE_PROTO_RANDOM_ALL \
+ (NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
struct nf_nat_ipv4_range {
unsigned int flags;
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
new file mode 100644
index 00000000..801bdd1e
--- /dev/null
+++ b/include/linux/netfilter/nf_tables.h
@@ -0,0 +1,793 @@
+#ifndef _LINUX_NF_TABLES_H
+#define _LINUX_NF_TABLES_H
+
+#define NFT_CHAIN_MAXNAMELEN 32
+#define NFT_USERDATA_MAXLEN 256
+
+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 - 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,
+ NFT_JUMP = -3,
+ NFT_GOTO = -4,
+ 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,
+ 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 - nf_tables generic list netlink attributes
+ *
+ * @NFTA_LIST_ELEM: list element (NLA_NESTED)
+ */
+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 - 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,
+ NFTA_HOOK_PRIORITY,
+ __NFTA_HOOK_MAX
+};
+#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 - 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)
+ * @NFTA_TABLE_USE: number of chains in this table (NLA_U32)
+ */
+enum nft_table_attributes {
+ NFTA_TABLE_UNSPEC,
+ NFTA_TABLE_NAME,
+ NFTA_TABLE_FLAGS,
+ NFTA_TABLE_USE,
+ __NFTA_TABLE_MAX
+};
+#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,
+ NFTA_CHAIN_HANDLE,
+ NFTA_CHAIN_NAME,
+ NFTA_CHAIN_HOOK,
+ 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)
+ * @NFTA_RULE_USERDATA: user data (NLA_BINARY, NFT_USERDATA_MAXLEN)
+ */
+enum nft_rule_attributes {
+ NFTA_RULE_UNSPEC,
+ NFTA_RULE_TABLE,
+ NFTA_RULE_CHAIN,
+ NFTA_RULE_HANDLE,
+ NFTA_RULE_EXPRESSIONS,
+ NFTA_RULE_COMPAT,
+ NFTA_RULE_POSITION,
+ NFTA_RULE_USERDATA,
+ __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,
+ 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
+ *
+ * @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_policies - set selection policy
+ *
+ * @NFT_SET_POL_PERFORMANCE: prefer high performance over low memory use
+ * @NFT_SET_POL_MEMORY: prefer low memory use over high performance
+ */
+enum nft_set_policies {
+ NFT_SET_POL_PERFORMANCE,
+ NFT_SET_POL_MEMORY,
+};
+
+/**
+ * enum nft_set_desc_attributes - set element description
+ *
+ * @NFTA_SET_DESC_SIZE: number of elements in set (NLA_U32)
+ */
+enum nft_set_desc_attributes {
+ NFTA_SET_DESC_UNSPEC,
+ NFTA_SET_DESC_SIZE,
+ __NFTA_SET_DESC_MAX
+};
+#define NFTA_SET_DESC_MAX (__NFTA_SET_DESC_MAX - 1)
+
+/**
+ * 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)
+ * @NFTA_SET_POLICY: selection policy (NLA_U32)
+ * @NFTA_SET_DESC: set description (NLA_NESTED)
+ * @NFTA_SET_ID: uniquely identifies a set in a transaction (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_POLICY,
+ NFTA_SET_DESC,
+ NFTA_SET_ID,
+ __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)
+ * @NFTA_SET_ELEM_LIST_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
+ */
+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_SET_ID,
+ __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 - 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,
+ NFTA_DATA_VERDICT,
+ __NFTA_DATA_MAX
+};
+#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,
+ NFTA_VERDICT_CHAIN,
+ __NFTA_VERDICT_MAX
+};
+#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,
+ NFTA_EXPR_DATA,
+ __NFTA_EXPR_MAX
+};
+#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,
+ NFTA_IMMEDIATE_DATA,
+ __NFTA_IMMEDIATE_MAX
+};
+#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,
+ 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 - 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,
+ 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 - 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,
+ NFT_CMP_LT,
+ NFT_CMP_LTE,
+ NFT_CMP_GT,
+ 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,
+ NFTA_CMP_OP,
+ NFTA_CMP_DATA,
+ __NFTA_CMP_MAX
+};
+#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)
+ * @NFTA_LOOKUP_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
+ */
+enum nft_lookup_attributes {
+ NFTA_LOOKUP_UNSPEC,
+ NFTA_LOOKUP_SET,
+ NFTA_LOOKUP_SREG,
+ NFTA_LOOKUP_DREG,
+ NFTA_LOOKUP_SET_ID,
+ __NFTA_LOOKUP_MAX
+};
+#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,
+ NFTA_PAYLOAD_BASE,
+ NFTA_PAYLOAD_OFFSET,
+ NFTA_PAYLOAD_LEN,
+ __NFTA_PAYLOAD_MAX
+};
+#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,
+ NFTA_EXTHDR_TYPE,
+ NFTA_EXTHDR_OFFSET,
+ NFTA_EXTHDR_LEN,
+ __NFTA_EXTHDR_MAX
+};
+#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)
+ * @NFT_META_NFPROTO: netfilter protocol
+ * @NFT_META_L4PROTO: layer 4 protocol number
+ * @NFT_META_BRI_IIFNAME: packet input bridge interface name
+ * @NFT_META_BRI_OIFNAME: packet output bridge interface name
+ */
+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,
+ NFT_META_NFPROTO,
+ NFT_META_L4PROTO,
+ NFT_META_BRI_IIFNAME,
+ NFT_META_BRI_OIFNAME,
+};
+
+/**
+ * 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)
+ * @NFTA_META_SREG: source register (NLA_U32)
+ */
+enum nft_meta_attributes {
+ NFTA_META_UNSPEC,
+ NFTA_META_DREG,
+ NFTA_META_KEY,
+ NFTA_META_SREG,
+ __NFTA_META_MAX
+};
+#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,
+ NFT_CT_STATUS,
+ NFT_CT_MARK,
+ NFT_CT_SECMARK,
+ NFT_CT_EXPIRATION,
+ NFT_CT_HELPER,
+ NFT_CT_L3PROTOCOL,
+ NFT_CT_SRC,
+ NFT_CT_DST,
+ NFT_CT_PROTOCOL,
+ NFT_CT_PROTO_SRC,
+ NFT_CT_PROTO_DST,
+ NFT_CT_LABELS,
+};
+
+/**
+ * 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)
+ * @NFTA_CT_SREG: source register (NLA_U32)
+ */
+enum nft_ct_attributes {
+ NFTA_CT_UNSPEC,
+ NFTA_CT_DREG,
+ NFTA_CT_KEY,
+ NFTA_CT_DIRECTION,
+ NFTA_CT_SREG,
+ __NFTA_CT_MAX
+};
+#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_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,
+ NFTA_COUNTER_PACKETS,
+ __NFTA_COUNTER_MAX
+};
+#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)
+ * @NFTA_LOG_LEVEL: log level (NLA_U32)
+ * @NFTA_LOG_FLAGS: logging flags (NLA_U32)
+ */
+enum nft_log_attributes {
+ NFTA_LOG_UNSPEC,
+ NFTA_LOG_GROUP,
+ NFTA_LOG_PREFIX,
+ NFTA_LOG_SNAPLEN,
+ NFTA_LOG_QTHRESHOLD,
+ NFTA_LOG_LEVEL,
+ NFTA_LOG_FLAGS,
+ __NFTA_LOG_MAX
+};
+#define NFTA_LOG_MAX (__NFTA_LOG_MAX - 1)
+
+/**
+ * enum nft_queue_attributes - nf_tables queue expression netlink attributes
+ *
+ * @NFTA_QUEUE_NUM: netlink queue to send messages to (NLA_U16)
+ * @NFTA_QUEUE_TOTAL: number of queues to load balance packets on (NLA_U16)
+ * @NFTA_QUEUE_FLAGS: various flags (NLA_U16)
+ */
+enum nft_queue_attributes {
+ NFTA_QUEUE_UNSPEC,
+ NFTA_QUEUE_NUM,
+ NFTA_QUEUE_TOTAL,
+ NFTA_QUEUE_FLAGS,
+ __NFTA_QUEUE_MAX
+};
+#define NFTA_QUEUE_MAX (__NFTA_QUEUE_MAX - 1)
+
+#define NFT_QUEUE_FLAG_BYPASS 0x01 /* for compatibility with v2 */
+#define NFT_QUEUE_FLAG_CPU_FANOUT 0x02 /* use current CPU (no hashing) */
+#define NFT_QUEUE_FLAG_MASK 0x03
+
+/**
+ * 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,
+ NFTA_REJECT_ICMP_CODE,
+ __NFTA_REJECT_MAX
+};
+#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_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)
+
+#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..06eea26b
--- /dev/null
+++ b/include/linux/netfilter/nfnetlink.h
@@ -0,0 +1,64 @@
+#ifndef _NFNETLINK_H
+#define _NFNETLINK_H
+#include <linux/types.h>
+#include <linux/netfilter/nfnetlink_compat.h>
+
+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
+
+/* Reserved control nfnetlink messages */
+#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE
+#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1
+
+#endif /* _NFNETLINK_H */
diff --git a/include/linux/netfilter/xt_CT.h b/include/linux/netfilter/xt_CT.h
index 54528fdd..c3907db5 100644
--- a/include/linux/netfilter/xt_CT.h
+++ b/include/linux/netfilter/xt_CT.h
@@ -6,6 +6,9 @@
enum {
XT_CT_NOTRACK = 1 << 0,
XT_CT_NOTRACK_ALIAS = 1 << 1,
+ XT_CT_ZONE_DIR_ORIG = 1 << 2,
+ XT_CT_ZONE_DIR_REPL = 1 << 3,
+ XT_CT_ZONE_MARK = 1 << 4,
};
struct xt_ct_target_info {
diff --git a/include/linux/netfilter/xt_NFLOG.h b/include/linux/netfilter/xt_NFLOG.h
index 87b58311..f3307073 100644
--- a/include/linux/netfilter/xt_NFLOG.h
+++ b/include/linux/netfilter/xt_NFLOG.h
@@ -6,9 +6,13 @@
#define XT_NFLOG_DEFAULT_GROUP 0x1
#define XT_NFLOG_DEFAULT_THRESHOLD 0
-#define XT_NFLOG_MASK 0x0
+#define XT_NFLOG_MASK 0x1
+
+/* This flag indicates that 'len' field in xt_nflog_info is set*/
+#define XT_NFLOG_F_COPY_LEN 0x1
struct xt_nflog_info {
+ /* 'len' will be used iff you set XT_NFLOG_F_COPY_LEN in flags */
__u32 len;
__u16 group;
__u16 threshold;
diff --git a/include/linux/netfilter/xt_SYNPROXY.h b/include/linux/netfilter/xt_SYNPROXY.h
new file mode 100644
index 00000000..2d59fbaa
--- /dev/null
+++ b/include/linux/netfilter/xt_SYNPROXY.h
@@ -0,0 +1,16 @@
+#ifndef _XT_SYNPROXY_H
+#define _XT_SYNPROXY_H
+
+#define XT_SYNPROXY_OPT_MSS 0x01
+#define XT_SYNPROXY_OPT_WSCALE 0x02
+#define XT_SYNPROXY_OPT_SACK_PERM 0x04
+#define XT_SYNPROXY_OPT_TIMESTAMP 0x08
+#define XT_SYNPROXY_OPT_ECN 0x10
+
+struct xt_synproxy_info {
+ __u8 options;
+ __u8 wscale;
+ __u16 mss;
+};
+
+#endif /* _XT_SYNPROXY_H */
diff --git a/include/linux/netfilter/xt_bpf.h b/include/linux/netfilter/xt_bpf.h
index 5dda450e..b97725af 100644
--- a/include/linux/netfilter/xt_bpf.h
+++ b/include/linux/netfilter/xt_bpf.h
@@ -2,16 +2,39 @@
#define _XT_BPF_H
#include <linux/filter.h>
+#include <linux/limits.h>
#include <linux/types.h>
#define XT_BPF_MAX_NUM_INSTR 64
+#define XT_BPF_PATH_MAX (XT_BPF_MAX_NUM_INSTR * sizeof(struct sock_filter))
+
+struct bpf_prog;
struct xt_bpf_info {
__u16 bpf_program_num_elem;
struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
/* only used in the kernel */
- struct sk_filter *filter __attribute__((aligned(8)));
+ struct bpf_prog *filter __attribute__((aligned(8)));
+};
+
+enum xt_bpf_modes {
+ XT_BPF_MODE_BYTECODE,
+ XT_BPF_MODE_FD_PINNED,
+ XT_BPF_MODE_FD_ELF,
+};
+
+struct xt_bpf_info_v1 {
+ __u16 mode;
+ __u16 bpf_program_num_elem;
+ __s32 fd;
+ union {
+ struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
+ char path[XT_BPF_PATH_MAX];
+ };
+
+ /* only used in the kernel */
+ struct bpf_prog *filter __attribute__((aligned(8)));
};
#endif /*_XT_BPF_H */
diff --git a/include/linux/netfilter/xt_cgroup.h b/include/linux/netfilter/xt_cgroup.h
new file mode 100644
index 00000000..7fe61ed0
--- /dev/null
+++ b/include/linux/netfilter/xt_cgroup.h
@@ -0,0 +1,24 @@
+#ifndef _XT_CGROUP_H
+#define _XT_CGROUP_H
+
+#include <linux/types.h>
+#include <linux/limits.h>
+
+struct xt_cgroup_info_v0 {
+ __u32 id;
+ __u32 invert;
+};
+
+struct xt_cgroup_info_v1 {
+ __u8 has_path;
+ __u8 has_classid;
+ __u8 invert_path;
+ __u8 invert_classid;
+ char path[PATH_MAX];
+ __u32 classid;
+
+ /* kernel internal data */
+ void *priv __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CGROUP_H */
diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h
index 141efbd1..d9808b5d 100644
--- a/include/linux/netfilter/xt_hashlimit.h
+++ b/include/linux/netfilter/xt_hashlimit.h
@@ -5,8 +5,10 @@
/* timings are in milliseconds. */
#define XT_HASHLIMIT_SCALE 10000
+#define XT_HASHLIMIT_SCALE_v2 1000000llu
/* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490
- seconds, or one packet every 59 hours. */
+ * seconds, or one packet every 59 hours.
+ */
/* packet length accounting is done in 16-byte steps */
#define XT_HASHLIMIT_BYTE_SHIFT 4
@@ -61,6 +63,20 @@ struct hashlimit_cfg1 {
__u8 srcmask, dstmask;
};
+struct hashlimit_cfg2 {
+ __u64 avg; /* Average secs between packets * scale */
+ __u64 burst; /* Period multiplier for upper limit. */
+ __u32 mode; /* bitmask of XT_HASHLIMIT_HASH_* */
+
+ /* user specified */
+ __u32 size; /* how many buckets */
+ __u32 max; /* max number of entries */
+ __u32 gc_interval; /* gc interval */
+ __u32 expire; /* when do entries expire? */
+
+ __u8 srcmask, dstmask;
+};
+
struct xt_hashlimit_mtinfo1 {
char name[IFNAMSIZ];
struct hashlimit_cfg1 cfg;
@@ -69,4 +85,12 @@ struct xt_hashlimit_mtinfo1 {
struct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));
};
+struct xt_hashlimit_mtinfo2 {
+ char name[NAME_MAX];
+ struct hashlimit_cfg2 cfg;
+
+ /* Used internally by the kernel */
+ struct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));
+};
+
#endif /*_XT_HASHLIMIT_H*/
diff --git a/include/linux/netfilter/xt_ipcomp.h b/include/linux/netfilter/xt_ipcomp.h
new file mode 100644
index 00000000..45c7e40e
--- /dev/null
+++ b/include/linux/netfilter/xt_ipcomp.h
@@ -0,0 +1,16 @@
+#ifndef _XT_IPCOMP_H
+#define _XT_IPCOMP_H
+
+#include <linux/types.h>
+
+struct xt_ipcomp {
+ __u32 spis[2]; /* Security Parameter Index */
+ __u8 invflags; /* Inverse flags */
+ __u8 hdrres; /* Test of the Reserved Filed */
+};
+
+/* Values for "invflags" field in struct xt_ipcomp. */
+#define XT_IPCOMP_INV_SPI 0x01 /* Invert the sense of spi. */
+#define XT_IPCOMP_INV_MASK 0x01 /* All possible flags. */
+
+#endif /*_XT_IPCOMP_H*/
diff --git a/include/linux/netfilter/xt_osf.h b/include/linux/netfilter/xt_osf.h
index 18afa495..d0c4c761 100644
--- a/include/linux/netfilter/xt_osf.h
+++ b/include/linux/netfilter/xt_osf.h
@@ -14,7 +14,7 @@
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _XT_OSF_H
diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h
index 964d3d42..4210c9bf 100644
--- a/include/linux/netfilter/xt_set.h
+++ b/include/linux/netfilter/xt_set.h
@@ -66,9 +66,28 @@ struct xt_set_info_target_v2 {
struct xt_set_info_match_v3 {
struct xt_set_info match_set;
+ struct ip_set_counter_match0 packets;
+ struct ip_set_counter_match0 bytes;
+ __u32 flags;
+};
+
+/* Revision 4 match */
+
+struct xt_set_info_match_v4 {
+ struct xt_set_info match_set;
struct ip_set_counter_match packets;
struct ip_set_counter_match bytes;
__u32 flags;
};
+/* Revision 3 target */
+
+struct xt_set_info_target_v3 {
+ struct xt_set_info add_set;
+ struct xt_set_info del_set;
+ struct xt_set_info map_set;
+ __u32 flags;
+ __u32 timeout;
+};
+
#endif /*_XT_SET_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 <linux/netfilter.h>
+
+/* 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 <linux/types.h>
+
+#include <linux/netfilter_arp.h>
+
+#include <linux/netfilter/x_tables.h>
+
+#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 <linux/netfilter_arp/arp_tables.h>
+
+#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 */
diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h
new file mode 100644
index 00000000..71e9299b
--- /dev/null
+++ b/include/linux/netfilter_bridge.h
@@ -0,0 +1,33 @@
+#ifndef __LINUX_BRIDGE_NETFILTER_H
+#define __LINUX_BRIDGE_NETFILTER_H
+
+/* bridge-specific defines for netfilter.
+ */
+#include <limits.h>
+
+/* Bridge Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_BR_PRE_ROUTING 0
+/* If the packet is destined for this box. */
+#define NF_BR_LOCAL_IN 1
+/* If the packet is destined for another interface. */
+#define NF_BR_FORWARD 2
+/* Packets coming from a local process. */
+#define NF_BR_LOCAL_OUT 3
+/* Packets about to hit the wire. */
+#define NF_BR_POST_ROUTING 4
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING 5
+#define NF_BR_NUMHOOKS 6
+
+enum nf_br_hook_priorities {
+ NF_BR_PRI_FIRST = INT_MIN,
+ NF_BR_PRI_FILTER_BRIDGED = -200,
+ NF_BR_PRI_FILTER_OTHER = 200,
+ NF_BR_PRI_NAT_DST_BRIDGED = -300,
+ NF_BR_PRI_NAT_DST_OTHER = 100,
+ NF_BR_PRI_NAT_SRC = 300,
+ NF_BR_PRI_LAST = INT_MAX,
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_802_3.h b/include/linux/netfilter_bridge/ebt_802_3.h
new file mode 100644
index 00000000..f37522aa
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_802_3.h
@@ -0,0 +1,63 @@
+#ifndef _UAPI__LINUX_BRIDGE_EBT_802_3_H
+#define _UAPI__LINUX_BRIDGE_EBT_802_3_H
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#define EBT_802_3_SAP 0x01
+#define EBT_802_3_TYPE 0x02
+
+#define EBT_802_3_MATCH "802_3"
+
+/*
+ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
+ * to discover what kind of packet we're carrying.
+ */
+#define CHECK_TYPE 0xaa
+
+/*
+ * Control field may be one or two bytes. If the first byte has
+ * the value 0x03 then the entire length is one byte, otherwise it is two.
+ * One byte controls are used in Unnumbered Information frames.
+ * Two byte controls are used in Numbered Information frames.
+ */
+#define IS_UI 0x03
+
+#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
+
+/* ui has one byte ctrl, ni has two */
+struct hdr_ui {
+ __u8 dsap;
+ __u8 ssap;
+ __u8 ctrl;
+ __u8 orig[3];
+ __be16 type;
+};
+
+struct hdr_ni {
+ __u8 dsap;
+ __u8 ssap;
+ __be16 ctrl;
+ __u8 orig[3];
+ __be16 type;
+};
+
+struct ebt_802_3_hdr {
+ __u8 daddr[ETH_ALEN];
+ __u8 saddr[ETH_ALEN];
+ __be16 len;
+ union {
+ struct hdr_ui ui;
+ struct hdr_ni ni;
+ } llc;
+};
+
+
+struct ebt_802_3_info {
+ __u8 sap;
+ __be16 type;
+ __u8 bitmask;
+ __u8 invflags;
+};
+
+#endif /* _UAPI__LINUX_BRIDGE_EBT_802_3_H */
diff --git a/include/linux/netfilter_bridge/ebt_ip.h b/include/linux/netfilter_bridge/ebt_ip.h
new file mode 100644
index 00000000..c4bbc41b
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_ip.h
@@ -0,0 +1,44 @@
+/*
+ * ebt_ip
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * April, 2002
+ *
+ * Changes:
+ * added ip-sport and ip-dport
+ * Innominate Security Technologies AG <mhopf@innominate.com>
+ * September, 2002
+ */
+
+#ifndef __LINUX_BRIDGE_EBT_IP_H
+#define __LINUX_BRIDGE_EBT_IP_H
+
+#include <linux/types.h>
+
+#define EBT_IP_SOURCE 0x01
+#define EBT_IP_DEST 0x02
+#define EBT_IP_TOS 0x04
+#define EBT_IP_PROTO 0x08
+#define EBT_IP_SPORT 0x10
+#define EBT_IP_DPORT 0x20
+#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
+ EBT_IP_SPORT | EBT_IP_DPORT )
+#define EBT_IP_MATCH "ip"
+
+/* the same values are used for the invflags */
+struct ebt_ip_info {
+ __be32 saddr;
+ __be32 daddr;
+ __be32 smsk;
+ __be32 dmsk;
+ __u8 tos;
+ __u8 protocol;
+ __u8 bitmask;
+ __u8 invflags;
+ __u16 sport[2];
+ __u16 dport[2];
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_mark_m.h b/include/linux/netfilter_bridge/ebt_mark_m.h
new file mode 100644
index 00000000..410f9e5a
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_mark_m.h
@@ -0,0 +1,16 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
+#define __LINUX_BRIDGE_EBT_MARK_M_H
+
+#include <linux/types.h>
+
+#define EBT_MARK_AND 0x01
+#define EBT_MARK_OR 0x02
+#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
+struct ebt_mark_m_info {
+ unsigned long mark, mask;
+ __u8 invert;
+ __u8 bitmask;
+};
+#define EBT_MARK_MATCH "mark_m"
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_mark_t.h b/include/linux/netfilter_bridge/ebt_mark_t.h
new file mode 100644
index 00000000..7d5a268a
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_mark_t.h
@@ -0,0 +1,23 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
+#define __LINUX_BRIDGE_EBT_MARK_T_H
+
+/* The target member is reused for adding new actions, the
+ * value of the real target is -1 to -NUM_STANDARD_TARGETS.
+ * For backward compatibility, the 4 lsb (2 would be enough,
+ * but let's play it safe) are kept to designate this target.
+ * The remaining bits designate the action. By making the set
+ * action 0xfffffff0, the result will look ok for older
+ * versions. [September 2006] */
+#define MARK_SET_VALUE (0xfffffff0)
+#define MARK_OR_VALUE (0xffffffe0)
+#define MARK_AND_VALUE (0xffffffd0)
+#define MARK_XOR_VALUE (0xffffffc0)
+
+struct ebt_mark_t_info {
+ unsigned long mark;
+ /* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */
+ int target;
+};
+#define EBT_MARK_TARGET "mark"
+
+#endif
diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
index 57fd82a1..38542b4f 100644
--- a/include/linux/netfilter_ipv4/ip_tables.h
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -73,12 +73,12 @@ struct ipt_ip {
unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
/* Protocol, 0 = ANY */
- u_int16_t proto;
+ __u16 proto;
/* Flags word */
- u_int8_t flags;
+ __u8 flags;
/* Inverse flags */
- u_int8_t invflags;
+ __u8 invflags;
};
/* Values for "flag" field in struct ipt_ip (general ip structure). */
@@ -106,9 +106,9 @@ struct ipt_entry {
unsigned int nfcache;
/* Size of ipt_entry + matches */
- u_int16_t target_offset;
+ __u16 target_offset;
/* Size of ipt_entry + matches + target */
- u_int16_t next_offset;
+ __u16 next_offset;
/* Back pointer */
unsigned int comefrom;
@@ -141,9 +141,9 @@ struct ipt_entry {
/* ICMP matching stuff */
struct ipt_icmp {
- u_int8_t type; /* type to match */
- u_int8_t code[2]; /* range of code */
- u_int8_t invflags; /* Inverse flags */
+ __u8 type; /* type to match */
+ __u8 code[2]; /* range of code */
+ __u8 invflags; /* Inverse flags */
};
/* Values for "inv" field for struct ipt_icmp. */
diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h
deleted file mode 100644
index a8551671..00000000
--- a/include/linux/netfilter_ipv4/ipt_SAME.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _IPT_SAME_H
-#define _IPT_SAME_H
-
-#include <linux/types.h>
-
-#define IPT_SAME_MAX_RANGE 10
-
-#define IPT_SAME_NODST 0x01
-
-struct ipt_same_info {
- unsigned char info;
- __u32 rangesize;
- __u32 ipnum;
- __u32 *iparray;
-
- /* hangs off end. */
- struct nf_nat_ipv4_range range[IPT_SAME_MAX_RANGE];
-};
-
-#endif /*_IPT_SAME_H*/
diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h
index 3f19a97d..640a1d09 100644
--- a/include/linux/netfilter_ipv6/ip6_tables.h
+++ b/include/linux/netfilter_ipv6/ip6_tables.h
@@ -73,14 +73,14 @@ struct ip6t_ip6 {
* MH do not match any packets.
* - You also need to set IP6T_FLAGS_PROTO to "flags" to check protocol.
*/
- u_int16_t proto;
+ __u16 proto;
/* TOS to match iff flags & IP6T_F_TOS */
- u_int8_t tos;
+ __u8 tos;
/* Flags word */
- u_int8_t flags;
+ __u8 flags;
/* Inverse flags */
- u_int8_t invflags;
+ __u8 invflags;
};
/* Values for "flag" field in struct ip6t_ip6 (general ip6 structure). */
@@ -110,9 +110,9 @@ struct ip6t_entry {
unsigned int nfcache;
/* Size of ipt_entry + matches */
- u_int16_t target_offset;
+ __u16 target_offset;
/* Size of ipt_entry + matches + target */
- u_int16_t next_offset;
+ __u16 next_offset;
/* Back pointer */
unsigned int comefrom;
@@ -176,11 +176,14 @@ struct ip6t_error {
#define IP6T_SO_GET_REVISION_TARGET (IP6T_BASE_CTL + 5)
#define IP6T_SO_GET_MAX IP6T_SO_GET_REVISION_TARGET
+/* obtain original address if REDIRECT'd connection */
+#define IP6T_SO_ORIGINAL_DST 80
+
/* ICMP matching stuff */
struct ip6t_icmp {
- u_int8_t type; /* type to match */
- u_int8_t code[2]; /* range of code */
- u_int8_t invflags; /* Inverse flags */
+ __u8 type; /* type to match */
+ __u8 code[2]; /* range of code */
+ __u8 invflags; /* Inverse flags */
};
/* Values for "inv" field for struct ipt_icmp. */
diff --git a/include/linux/netfilter_ipv6/ip6t_REJECT.h b/include/linux/netfilter_ipv6/ip6t_REJECT.h
index 205ed62e..cd2e940c 100644
--- a/include/linux/netfilter_ipv6/ip6t_REJECT.h
+++ b/include/linux/netfilter_ipv6/ip6t_REJECT.h
@@ -10,7 +10,9 @@ enum ip6t_reject_with {
IP6T_ICMP6_ADDR_UNREACH,
IP6T_ICMP6_PORT_UNREACH,
IP6T_ICMP6_ECHOREPLY,
- IP6T_TCP_RESET
+ IP6T_TCP_RESET,
+ IP6T_ICMP6_POLICY_FAIL,
+ IP6T_ICMP6_REJECT_ROUTE
};
struct ip6t_reject_info {
diff --git a/include/xtables.h b/include/xtables.h
index c35a6e6d..e9bc3b7d 100644
--- a/include/xtables.h
+++ b/include/xtables.h
@@ -205,9 +205,24 @@ enum xtables_ext_flags {
XTABLES_EXT_ALIAS = 1 << 0,
};
+struct xt_xlate;
+
+struct xt_xlate_mt_params {
+ const void *ip;
+ const struct xt_entry_match *match;
+ int numeric;
+ bool escape_quotes;
+};
+
+struct xt_xlate_tg_params {
+ const void *ip;
+ const struct xt_entry_target *target;
+ int numeric;
+ bool escape_quotes;
+};
+
/* Include file for additions: new matches and targets. */
-struct xtables_match
-{
+struct xtables_match {
/*
* ABI/API version this module requires. Must be first member,
* as the rest of this struct may be subject to ABI changes.
@@ -220,17 +235,17 @@ struct xtables_match
const char *real_name;
/* Revision of match (0 by default). */
- u_int8_t revision;
+ uint8_t revision;
/* Extension flags */
- u_int8_t ext_flags;
+ uint8_t ext_flags;
- u_int16_t family;
+ uint16_t family;
/* Size of match data. */
size_t size;
- /* Size of match data relevent for userspace comparison purposes */
+ /* Size of match data relevant for userspace comparison purposes */
size_t userspacesize;
/* Function which prints out usage message. */
@@ -269,6 +284,10 @@ struct xtables_match
void (*x6_fcheck)(struct xt_fcheck_call *);
const struct xt_option_entry *x6_options;
+ /* Translate iptables to nft */
+ int (*xlate)(struct xt_xlate *xl,
+ const struct xt_xlate_mt_params *params);
+
/* Size of per-extension instance extra "global" scratch space */
size_t udata_size;
@@ -280,8 +299,7 @@ struct xtables_match
unsigned int loaded; /* simulate loading so options are merged properly */
};
-struct xtables_target
-{
+struct xtables_target {
/*
* ABI/API version this module requires. Must be first member,
* as the rest of this struct may be subject to ABI changes.
@@ -297,18 +315,18 @@ struct xtables_target
const char *real_name;
/* Revision of target (0 by default). */
- u_int8_t revision;
+ uint8_t revision;
/* Extension flags */
- u_int8_t ext_flags;
+ uint8_t ext_flags;
- u_int16_t family;
+ uint16_t family;
/* Size of target data. */
size_t size;
- /* Size of target data relevent for userspace comparison purposes */
+ /* Size of target data relevant for userspace comparison purposes */
size_t userspacesize;
/* Function which prints out usage message. */
@@ -346,6 +364,10 @@ struct xtables_target
void (*x6_fcheck)(struct xt_fcheck_call *);
const struct xt_option_entry *x6_options;
+ /* Translate iptables to nft */
+ int (*xlate)(struct xt_xlate *xl,
+ const struct xt_xlate_tg_params *params);
+
size_t udata_size;
/* Ignore these men behind the curtain: */
@@ -373,7 +395,7 @@ struct xtables_rule_match {
*/
struct xtables_pprot {
const char *name;
- u_int8_t num;
+ uint8_t num;
};
enum xtables_tryload {
@@ -401,10 +423,22 @@ 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}
+/*
+ * enum op-
+ *
+ * For writing clean nftables translations code
+ */
+enum xt_op {
+ XT_OP_EQ,
+ XT_OP_NEQ,
+ XT_OP_MAX,
+};
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -432,6 +466,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);
@@ -446,12 +482,12 @@ extern bool xtables_strtoul(const char *, char **, uintmax_t *,
extern bool xtables_strtoui(const char *, char **, unsigned int *,
unsigned int, unsigned int);
extern int xtables_service_to_port(const char *name, const char *proto);
-extern u_int16_t xtables_parse_port(const char *port, const char *proto);
+extern uint16_t xtables_parse_port(const char *port, const char *proto);
extern void
xtables_parse_interface(const char *arg, char *vianame, unsigned char *mask);
/* this is a special 64bit data type that is 8-byte aligned */
-#define aligned_u64 u_int64_t __attribute__((aligned(8)))
+#define aligned_u64 uint64_t __attribute__((aligned(8)))
extern struct xtables_globals *xt_params;
#define xtables_error (xt_params->exit_err)
@@ -514,7 +550,7 @@ extern void xtables_print_num(uint64_t number, unsigned int format);
#endif
extern const struct xtables_pprot xtables_chain_protos[];
-extern u_int16_t xtables_parse_protocol(const char *s);
+extern uint16_t xtables_parse_protocol(const char *s);
/* kernel revision handling */
extern int kernel_version;
@@ -545,6 +581,14 @@ extern void xtables_lmap_free(struct xtables_lmap *);
extern int xtables_lmap_name2id(const struct xtables_lmap *, const char *);
extern const char *xtables_lmap_id2name(const struct xtables_lmap *, int);
+/* xlate infrastructure */
+struct xt_xlate *xt_xlate_alloc(int size);
+void xt_xlate_free(struct xt_xlate *xl);
+void xt_xlate_add(struct xt_xlate *xl, const char *fmt, ...);
+void xt_xlate_add_comment(struct xt_xlate *xl, const char *comment);
+const char *xt_xlate_get_comment(struct xt_xlate *xl);
+const char *xt_xlate_get(struct xt_xlate *xl);
+
#ifdef XTABLES_INTERNAL
/* Shipped modules rely on this... */
diff --git a/iptables-test.py b/iptables-test.py
new file mode 100755
index 00000000..9e137f8c
--- /dev/null
+++ b/iptables-test.py
@@ -0,0 +1,311 @@
+#!/usr/bin/python
+#
+# (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+#
+# 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 <http://www.sophos.com>
+#
+
+import sys
+import os
+import subprocess
+import argparse
+
+IPTABLES = "iptables"
+IP6TABLES = "ip6tables"
+#IPTABLES = "xtables -4"
+#IP6TABLES = "xtables -6"
+
+IPTABLES_SAVE = "iptables-save"
+IP6TABLES_SAVE = "ip6tables-save"
+#IPTABLES_SAVE = ['xtables-save','-4']
+#IP6TABLES_SAVE = ['xtables-save','-6']
+
+EXTENSIONS_PATH = "extensions"
+LOGFILE="/tmp/iptables-test.log"
+log_file = None
+
+
+class Colors:
+ HEADER = '\033[95m'
+ BLUE = '\033[94m'
+ GREEN = '\033[92m'
+ YELLOW = '\033[93m'
+ RED = '\033[91m'
+ ENDC = '\033[0m'
+
+
+def print_error(reason, filename=None, lineno=None):
+ '''
+ Prints an error with nice colors, indicating file and line number.
+ '''
+ print (filename + ": " + Colors.RED + "ERROR" +
+ Colors.ENDC + ": line %d (%s)" % (lineno, reason))
+
+
+def delete_rule(iptables, rule, filename, lineno):
+ '''
+ Removes an iptables rule
+ '''
+ cmd = iptables + " -D " + rule
+ ret = execute_cmd(cmd, filename, lineno)
+ if ret == 1:
+ reason = "cannot delete: " + iptables + " -I " + rule
+ print_error(reason, filename, lineno)
+ return -1
+
+ return 0
+
+
+def run_test(iptables, rule, rule_save, res, filename, lineno):
+ '''
+ Executes an unit test. Returns the output of delete_rule().
+
+ Parameters:
+ :param iptables: string with the iptables command to execute
+ :param rule: string with iptables arguments for the rule to test
+ :param rule_save: string to find the rule in the output of iptables -save
+ :param res: expected result of the rule. Valid values: "OK", "FAIL"
+ :param filename: name of the file tested (used for print_error purposes)
+ :param lineno: line number being tested (used for print_error purposes)
+ '''
+ ret = 0
+
+ cmd = iptables + " -A " + rule
+ ret = execute_cmd(cmd, filename, lineno)
+
+ #
+ # report failed test
+ #
+ if ret:
+ if res == "OK":
+ reason = "cannot load: " + cmd
+ print_error(reason, filename, lineno)
+ return -1
+ else:
+ # do not report this error
+ return 0
+ else:
+ if res == "FAIL":
+ reason = "should fail: " + cmd
+ print_error(reason, filename, lineno)
+ delete_rule(iptables, rule, filename, lineno)
+ return -1
+
+ matching = 0
+ splitted = iptables.split(" ")
+ if len(splitted) == 2:
+ if splitted[1] == '-4':
+ command = IPTABLES_SAVE
+ elif splitted[1] == '-6':
+ command = IP6TABLES_SAVE
+ elif len(splitted) == 1:
+ if splitted[0] == IPTABLES:
+ command = IPTABLES_SAVE
+ elif splitted[0] == IP6TABLES:
+ command = IP6TABLES_SAVE
+ args = splitted[1:]
+ proc = subprocess.Popen(command, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = proc.communicate()
+
+ #
+ # check for segfaults
+ #
+ if proc.returncode == -11:
+ reason = "iptables-save segfaults: " + cmd
+ print_error(reason, filename, lineno)
+ delete_rule(iptables, rule, filename, lineno)
+ return -1
+
+ # find the rule
+ matching = out.find(rule_save)
+ if matching < 0:
+ reason = "cannot find: " + iptables + " -I " + rule
+ print_error(reason, filename, lineno)
+ delete_rule(iptables, rule, filename, lineno)
+ return -1
+
+ return delete_rule(iptables, rule, filename, lineno)
+
+
+def execute_cmd(cmd, filename, lineno):
+ '''
+ Executes a command, checking for segfaults and returning the command exit
+ code.
+
+ :param cmd: string with the command to be executed
+ :param filename: name of the file tested (used for print_error purposes)
+ :param lineno: line number being tested (used for print_error purposes)
+ '''
+ global log_file
+ print >> log_file, "command: %s" % cmd
+ ret = subprocess.call(cmd, shell=True, universal_newlines=True,
+ stderr=subprocess.STDOUT, stdout=log_file)
+ log_file.flush()
+
+ # generic check for segfaults
+ if ret == -11:
+ reason = "command segfaults: " + cmd
+ print_error(reason, filename, lineno)
+ return ret
+
+
+def run_test_file(filename):
+ '''
+ Runs a test file
+
+ :param filename: name of the file with the test rules
+ '''
+ #
+ # if this is not a test file, skip.
+ #
+ if not filename.endswith(".t"):
+ return 0, 0
+
+ if "libipt_" in filename:
+ iptables = IPTABLES
+ elif "libip6t_" in filename:
+ iptables = IP6TABLES
+ elif "libxt_" in filename:
+ iptables = IPTABLES
+ else:
+ # default to iptables if not known prefix
+ iptables = IPTABLES
+
+ f = open(filename)
+
+ tests = 0
+ passed = 0
+ table = ""
+ total_test_passed = True
+
+ for lineno, line in enumerate(f):
+ if line[0] == "#":
+ continue
+
+ if line[0] == ":":
+ chain_array = line.rstrip()[1:].split(",")
+ continue
+
+ # external non-iptables invocation, executed as is.
+ if line[0] == "@":
+ external_cmd = line.rstrip()[1:]
+ execute_cmd(external_cmd, filename, lineno)
+ continue
+
+ if line[0] == "*":
+ table = line.rstrip()[1:]
+ continue
+
+ if len(chain_array) == 0:
+ print "broken test, missing chain, leaving"
+ sys.exit()
+
+ test_passed = True
+ tests += 1
+
+ for chain in chain_array:
+ item = line.split(";")
+ if table == "":
+ rule = chain + " " + item[0]
+ else:
+ rule = chain + " -t " + table + " " + item[0]
+
+ if item[1] == "=":
+ rule_save = chain + " " + item[0]
+ else:
+ rule_save = chain + " " + item[1]
+
+ res = item[2].rstrip()
+
+ ret = run_test(iptables, rule, rule_save,
+ res, filename, lineno + 1)
+ if ret < 0:
+ test_passed = False
+ total_test_passed = False
+ break
+
+ if test_passed:
+ passed += 1
+
+ if total_test_passed:
+ print filename + ": " + Colors.GREEN + "OK" + Colors.ENDC
+
+ f.close()
+ return tests, passed
+
+
+def show_missing():
+ '''
+ Show the list of missing test files
+ '''
+ file_list = os.listdir(EXTENSIONS_PATH)
+ testfiles = [i for i in file_list if i.endswith('.t')]
+ libfiles = [i for i in file_list
+ if i.startswith('lib') and i.endswith('.c')]
+
+ def test_name(x):
+ return x[0:-2] + '.t'
+ missing = [test_name(i) for i in libfiles
+ if not test_name(i) in testfiles]
+
+ print '\n'.join(missing)
+
+
+#
+# main
+#
+def main():
+ parser = argparse.ArgumentParser(description='Run iptables tests')
+ parser.add_argument('filename', nargs='?',
+ metavar='path/to/file.t',
+ help='Run only this test')
+ parser.add_argument('-m', '--missing', action='store_true',
+ help='Check for missing tests')
+ args = parser.parse_args()
+
+ #
+ # show list of missing test files
+ #
+ if args.missing:
+ show_missing()
+ return
+
+ if os.getuid() != 0:
+ print "You need to be root to run this, sorry"
+ return
+
+ test_files = 0
+ tests = 0
+ passed = 0
+
+ # setup global var log file
+ global log_file
+ try:
+ log_file = open(LOGFILE, 'w')
+ except IOError:
+ print "Couldn't open log file %s" % LOGFILE
+ return
+
+ file_list = [os.path.join(EXTENSIONS_PATH, i)
+ for i in os.listdir(EXTENSIONS_PATH)]
+ if args.filename:
+ file_list = [args.filename]
+ for filename in file_list:
+ file_tests, file_passed = run_test_file(filename)
+ if file_tests:
+ tests += file_tests
+ passed += file_passed
+ test_files += 1
+
+ print ("%d test files, %d unit tests, %d passed" %
+ (test_files, tests, passed))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/iptables/.gitignore b/iptables/.gitignore
index c9c31788..7438ad12 100644
--- a/iptables/.gitignore
+++ b/iptables/.gitignore
@@ -1,5 +1,4 @@
/ip6tables
-/ip6tables.8
/ip6tables-save
/ip6tables-restore
/ip6tables-static
@@ -8,9 +7,15 @@
/iptables-extensions.8
/iptables-extensions.8.tmpl
/iptables-save
+/iptables-save.8
/iptables-restore
+/iptables-restore.8
/iptables-static
/iptables-xml
+/iptables-xml.1
/xtables-multi
+/xtables-config-parser.c
+/xtables-config-parser.h
+/xtables-config-syntax.c
/xtables.pc
diff --git a/iptables/Android.mk b/iptables/Android.mk
index a6f2ae6f..52ecc21f 100644
--- a/iptables/Android.mk
+++ b/iptables/Android.mk
@@ -9,13 +9,24 @@ commonFlags:= \
-Werror
#----------------------------------------------------------------
-# iptables
+# The iptables lock file
+include $(CLEAR_VARS)
+LOCAL_MODULE := xtables.lock
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+
+include $(BUILD_PREBUILT)
+
+#----------------------------------------------------------------
+# iptables
include $(CLEAR_VARS)
LOCAL_C_INCLUDES:= \
- $(LOCAL_PATH)/../include/
+ $(LOCAL_PATH)/../include/ \
+ $(LOCAL_PATH)/../
LOCAL_CFLAGS:=-DNO_SHARED_LIBS=1
LOCAL_CFLAGS+=-DALL_INCLUSIVE
@@ -52,7 +63,8 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES:= \
- $(LOCAL_PATH)/../include/
+ $(LOCAL_PATH)/../include/ \
+ $(LOCAL_PATH)/../
LOCAL_CFLAGS:=-DNO_SHARED_LIBS=1
LOCAL_CFLAGS+=-DALL_INCLUSIVE
@@ -84,5 +96,4 @@ LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
include $(BUILD_EXECUTABLE)
-
#----------------------------------------------------------------
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index 501e8255..f92cc4ff 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -1,7 +1,10 @@
# -*- 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 -I${top_srcdir} ${kinclude_CPPFLAGS} ${libmnl_CFLAGS} ${libnftnl_CFLAGS} ${libnetfilter_conntrack_CFLAGS}
+AM_YFLAGS = -d
+
+BUILT_SOURCES =
xtables_multi_SOURCES = xtables-multi.c iptables-xml.c
xtables_multi_CFLAGS = ${AM_CFLAGS}
@@ -24,11 +27,40 @@ endif
xtables_multi_SOURCES += xshared.c
xtables_multi_LDADD += ../libxtables/libxtables.la -lm
+# nftables compatibility layer
+if ENABLE_NFTABLES
+BUILT_SOURCES += xtables-config-parser.h
+xtables_compat_multi_SOURCES = xtables-compat-multi.c iptables-xml.c
+xtables_compat_multi_CFLAGS = ${AM_CFLAGS}
+xtables_compat_multi_LDADD = ../extensions/libext.a ../extensions/libext_ebt.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-arp-standalone.c xtables-arp.c \
+ getethertype.c nft-bridge.c \
+ xtables-eb-standalone.c xtables-eb.c \
+ xtables-translate.c
+xtables_compat_multi_LDADD += ${libmnl_LIBS} ${libnftnl_LIBS} ${libnetfilter_conntrack_LIBS} ../extensions/libext4.a ../extensions/libext6.a ../extensions/libext_ebt.a ../extensions/libext_arpt.a
+# 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
-CLEANFILES = iptables.8 ip6tables.8
+CLEANFILES = iptables.8 \
+ xtables-config-parser.c xtables-config-syntax.c
vx_bin_links = iptables-xml
if ENABLE_IPV4
@@ -37,6 +69,13 @@ endif
if ENABLE_IPV6
v6_sbin_links = ip6tables ip6tables-restore ip6tables-save
endif
+if ENABLE_NFTABLES
+x_sbin_links = iptables-compat iptables-compat-restore iptables-compat-save \
+ ip6tables-compat ip6tables-compat-restore ip6tables-compat-save \
+ iptables-translate ip6tables-translate \
+ iptables-restore-translate ip6tables-restore-translate \
+ arptables-compat ebtables-compat
+endif
iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man
${AM_VERBOSE_GEN} sed \
@@ -52,3 +91,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-compat-multi "${DESTDIR}${sbindir}/$$i"; done;
diff --git a/iptables/getethertype.c b/iptables/getethertype.c
new file mode 100644
index 00000000..027ef4ad
--- /dev/null
+++ b/iptables/getethertype.c
@@ -0,0 +1,161 @@
+/*
+* getethertype.c
+*
+* This file was part of the NYS Library.
+*
+** The NYS Library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Library 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/********************************************************************
+* Description: Ethertype name service switch and the ethertypes
+* database access functions
+* Author: Nick Fedchik <fnm@ukrsat.com>
+* Checker: Bart De Schuymer <bdschuym@pandora.be>
+* Origin: uClibc-0.9.16/libc/inet/getproto.c
+* Created at: Mon Nov 11 12:20:11 EET 2002
+********************************************************************/
+
+#include <ctype.h>
+#include <features.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+
+#include <ebtables/ethernetdb.h>
+
+#define MAXALIASES 35
+
+static FILE *etherf = NULL;
+static char line[BUFSIZ + 1];
+static struct ethertypeent et_ent;
+static char *ethertype_aliases[MAXALIASES];
+static int ethertype_stayopen;
+
+void setethertypeent(int f)
+{
+ if (etherf == NULL)
+ etherf = fopen(_PATH_ETHERTYPES, "r");
+ else
+ rewind(etherf);
+ ethertype_stayopen |= f;
+}
+
+void endethertypeent(void)
+{
+ if (etherf) {
+ fclose(etherf);
+ etherf = NULL;
+ }
+ ethertype_stayopen = 0;
+}
+
+struct ethertypeent *getethertypeent(void)
+{
+ char *e;
+ char *endptr;
+ register char *cp, **q;
+
+ if (etherf == NULL
+ && (etherf = fopen(_PATH_ETHERTYPES, "r")) == NULL) {
+ return (NULL);
+ }
+
+again:
+ if ((e = fgets(line, BUFSIZ, etherf)) == NULL) {
+ return (NULL);
+ }
+ if (*e == '#')
+ goto again;
+ cp = strpbrk(e, "#\n");
+ if (cp == NULL)
+ goto again;
+ *cp = '\0';
+ et_ent.e_name = e;
+ cp = strpbrk(e, " \t");
+ if (cp == NULL)
+ goto again;
+ *cp++ = '\0';
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ e = strpbrk(cp, " \t");
+ if (e != NULL)
+ *e++ = '\0';
+// Check point
+ et_ent.e_ethertype = strtol(cp, &endptr, 16);
+ if (*endptr != '\0'
+ || (et_ent.e_ethertype < ETH_ZLEN
+ || et_ent.e_ethertype > 0xFFFF))
+ goto again; // Skip invalid etherproto type entry
+ q = et_ent.e_aliases = ethertype_aliases;
+ if (e != NULL) {
+ cp = e;
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ if (q < &ethertype_aliases[MAXALIASES - 1])
+ *q++ = cp;
+ cp = strpbrk(cp, " \t");
+ if (cp != NULL)
+ *cp++ = '\0';
+ }
+ }
+ *q = NULL;
+ return (&et_ent);
+}
+
+
+struct ethertypeent *getethertypebyname(const char *name)
+{
+ register struct ethertypeent *e;
+ register char **cp;
+
+ setethertypeent(ethertype_stayopen);
+ while ((e = getethertypeent()) != NULL) {
+ if (strcasecmp(e->e_name, name) == 0)
+ break;
+ for (cp = e->e_aliases; *cp != 0; cp++)
+ if (strcasecmp(*cp, name) == 0)
+ goto found;
+ }
+found:
+ if (!ethertype_stayopen)
+ endethertypeent();
+ return (e);
+}
+
+struct ethertypeent *getethertypebynumber(int type)
+{
+ register struct ethertypeent *e;
+
+ setethertypeent(ethertype_stayopen);
+ while ((e = getethertypeent()) != NULL)
+ if (e->e_ethertype == type)
+ break;
+ if (!ethertype_stayopen)
+ endethertypeent();
+ return (e);
+}
diff --git a/iptables/ip6tables-restore.8 b/iptables/ip6tables-restore.8
index dbe19daf..cf4ea3e7 100644
--- a/iptables/ip6tables-restore.8
+++ b/iptables/ip6tables-restore.8
@@ -1,68 +1 @@
-.TH IP6TABLES-RESTORE 8 "Jan 30, 2002" "" ""
-.\"
-.\" Man page written by Harald Welte <laforge@gnumonks.org>
-.\" It is based on the iptables man page.
-.\"
-.\" 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.
-.\"
-.\"
-.SH NAME
-ip6tables-restore \(em Restore IPv6 Tables
-.SH SYNOPSIS
-\fBip6tables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
-[\fB\-T\fP \fIname\fP]
-.SH DESCRIPTION
-.PP
-.B ip6tables-restore
-is used to restore IPv6 Tables from data specified on STDIN. Use
-I/O redirection provided by your shell to read from a file
-.TP
-\fB\-c\fR, \fB\-\-counters\fR
-restore the values of all packet and byte counters
-.TP
-\fB\-h\fP, \fB\-\-help\fP
-Print a short option summary.
-.TP
-\fB\-n\fR, \fB\-\-noflush\fR
-don't flush the previous contents of the table. If not specified,
-\fBip6tables-restore\fP flushes (deletes) all previous contents of the
-respective table.
-.TP
-\fB\-t\fP, \fB\-\-test\fP
-Only parse and construct the ruleset, but do not commit it.
-.TP
-\fB\-v\fP, \fB\-\-verbose\fP
-Print additional debug info during ruleset processing.
-.TP
-\fB\-M\fP, \fB\-\-modprobe\fP \fImodprobe_program\fP
-Specify the path to the modprobe program. By default, ip6tables-restore will
-inspect /proc/sys/kernel/modprobe to determine the executable's path.
-.TP
-\fB\-T\fP, \fB\-\-table\fP \fIname\fP
-Restore only the named table even if the input stream contains other ones.
-.B ip6tables-restore
-flushes (deletes) all previous contents of the respective IPv6 Table.
-.SH BUGS
-None known as of iptables-1.2.1 release
-.SH AUTHORS
-Harald Welte <laforge@gnumonks.org>
-.br
-Andras Kis-Szabo <kisza@sch.bme.hu>
-.SH SEE ALSO
-\fBip6tables\-save\fP(8), \fBip6tables\fP(8)
-.PP
-The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
-which details NAT, and the netfilter-hacking-HOWTO which details the
-internals.
+.so man8/iptables-restore.8
diff --git a/iptables/ip6tables-restore.c b/iptables/ip6tables-restore.c
index 8d097984..7d405906 100644
--- a/iptables/ip6tables-restore.c
+++ b/iptables/ip6tables-restore.c
@@ -9,7 +9,7 @@
*/
#include <getopt.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
@@ -26,19 +26,23 @@
#define DEBUGP(x, args...)
#endif
-static int binary = 0, counters = 0, verbose = 0, noflush = 0, wait = 0;
+static int counters = 0, verbose = 0, noflush = 0, wait = 0;
+
+static struct timeval wait_interval = {
+ .tv_sec = 1,
+};
/* 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 = "wait", .has_arg = false, .val = 'w'},
- {.name = "modprobe", .has_arg = true, .val = 'M'},
- {.name = "table", .has_arg = true, .val = 'T'},
+ {.name = "counters", .has_arg = 0, .val = 'c'},
+ {.name = "verbose", .has_arg = 0, .val = 'v'},
+ {.name = "test", .has_arg = 0, .val = 't'},
+ {.name = "help", .has_arg = 0, .val = 'h'},
+ {.name = "noflush", .has_arg = 0, .val = 'n'},
+ {.name = "modprobe", .has_arg = 1, .val = 'M'},
+ {.name = "table", .has_arg = 1, .val = 'T'},
+ {.name = "wait", .has_arg = 2, .val = 'w'},
+ {.name = "wait-interval", .has_arg = 2, .val = 'W'},
{NULL},
};
@@ -46,15 +50,16 @@ static void print_usage(const char *name, const char *version) __attribute__((no
static void print_usage(const char *name, const char *version)
{
- fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h] [-w]\n"
- " [ --binary ]\n"
+ fprintf(stderr, "Usage: %s [-c] [-v] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command]\n"
" [ --counters ]\n"
" [ --verbose ]\n"
" [ --test ]\n"
" [ --help ]\n"
" [ --noflush ]\n"
- " [ --wait ]\n"
- " [ --modprobe=<command>]\n", name);
+ " [ --wait=<seconds>\n"
+ " [ --wait-interval=<usecs>\n"
+ " [ --table=<TABLE> ]\n"
+ " [ --modprobe=<command> ]\n", name);
exit(1);
}
@@ -208,10 +213,10 @@ int ip6tables_restore_main(int argc, char *argv[])
init_extensions6();
#endif
- while ((c = getopt_long(argc, argv, "bcvthnwM:T:", options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "bcvthnwWM:T:", options, NULL)) != -1) {
switch (c) {
case 'b':
- binary = 1;
+ fprintf(stderr, "-b/--binary option is not implemented\n");
break;
case 'c':
counters = 1;
@@ -230,7 +235,10 @@ int ip6tables_restore_main(int argc, char *argv[])
noflush = 1;
break;
case 'w':
- wait = 1;
+ wait = parse_wait_time(argc, argv);
+ break;
+ case 'W':
+ parse_wait_interval(argc, argv, &wait_interval);
break;
case 'M':
xtables_modprobe_program = optarg;
@@ -279,14 +287,16 @@ int ip6tables_restore_main(int argc, char *argv[])
ret = 1;
}
- /* Release the lock since we're done with the current table. */
+ /* Done with the current table, release the lock. */
if (lock >= 0) {
xtables_unlock(lock);
+ lock = XT_LOCK_NOT_ACQUIRED;
}
+
in_table = 0;
} else if ((buffer[0] == '*') && (!in_table)) {
/* Acquire a lock before we create a new table handle */
- lock = xtables_lock(wait);
+ lock = xtables_lock(wait, &wait_interval);
if (lock == XT_LOCK_BUSY) {
fprintf(stderr, "Another app is currently holding the xtables lock. "
"Perhaps you want to use the -w option?\n");
diff --git a/iptables/ip6tables-save.8 b/iptables/ip6tables-save.8
index 457be821..182f55c1 100644
--- a/iptables/ip6tables-save.8
+++ b/iptables/ip6tables-save.8
@@ -1,53 +1 @@
-.TH IP6TABLES-SAVE 8 "Jan 30, 2002" "" ""
-.\"
-.\" Man page written by Harald Welte <laforge@gnumonks.org>
-.\" It is based on the iptables man page.
-.\"
-.\" 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.
-.\"
-.\"
-.SH NAME
-ip6tables-save \(em dump iptables rules to stdout
-.SH SYNOPSIS
-\fBip6tables\-save\fP [\fB\-M\fP \fImodprobe\fP] [\fB\-c\fP]
-[\fB\-t\fP \fItable\fP
-.SH DESCRIPTION
-.PP
-.B ip6tables-save
-is used to dump the contents of an IPv6 Table in easily parseable format
-to STDOUT. Use I/O-redirection provided by your shell to write to a file.
-.TP
-\fB\-M\fP \fImodprobe_program\fP
-Specify the path to the modprobe program. By default, iptables-save will
-inspect /proc/sys/kernel/modprobe to determine the executable's path.
-.TP
-\fB\-c\fR, \fB\-\-counters\fR
-include the current values of all packet and byte counters in the output
-.TP
-\fB\-t\fR, \fB\-\-table\fR \fItablename\fP
-restrict output to only one table. If not specified, output includes all
-available tables.
-.SH BUGS
-None known as of iptables-1.2.1 release
-.SH AUTHORS
-Harald Welte <laforge@gnumonks.org>
-.br
-Andras Kis-Szabo <kisza@sch.bme.hu>
-.SH SEE ALSO
-\fBip6tables\-restore\fP(8), \fBip6tables\fP(8)
-.PP
-The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
-which details NAT, and the netfilter-hacking-HOWTO which details the
-internals.
+.so man8/iptables-save.8
diff --git a/iptables/ip6tables-save.c b/iptables/ip6tables-save.c
index d819b30b..f35e921e 100644
--- a/iptables/ip6tables-save.c
+++ b/iptables/ip6tables-save.c
@@ -6,7 +6,7 @@
* This code is distributed under the terms of GNU GPL v2
*/
#include <getopt.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -18,10 +18,6 @@
#include "ip6tables.h"
#include "ip6tables-multi.h"
-#ifndef NO_SHARED_LIBS
-#include <dlfcn.h>
-#endif
-
static int show_counters = 0;
static const struct option options[] = {
@@ -141,8 +137,11 @@ int ip6tables_save_main(int argc, char *argv[])
init_extensions6();
#endif
- while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "bcdt:M:", options, NULL)) != -1) {
switch (c) {
+ case 'b':
+ fprintf(stderr, "-b/--binary option is not implemented\n");
+ break;
case 'c':
show_counters = 1;
break;
diff --git a/iptables/ip6tables-standalone.c b/iptables/ip6tables-standalone.c
index 656e08d5..35d2d9a5 100644
--- a/iptables/ip6tables-standalone.c
+++ b/iptables/ip6tables-standalone.c
@@ -73,6 +73,8 @@ ip6tables_main(int argc, char *argv[])
fprintf(stderr, "ip6tables: %s.\n",
ip6tc_strerror(errno));
}
+ if (errno == EAGAIN)
+ exit(RESOURCE_PROBLEM);
}
exit(!ret);
diff --git a/iptables/ip6tables.8 b/iptables/ip6tables.8
new file mode 100644
index 00000000..0dee41ad
--- /dev/null
+++ b/iptables/ip6tables.8
@@ -0,0 +1 @@
+.so man8/iptables.8
diff --git a/iptables/ip6tables.8.in b/iptables/ip6tables.8.in
deleted file mode 100644
index 5b2a7d76..00000000
--- a/iptables/ip6tables.8.in
+++ /dev/null
@@ -1,463 +0,0 @@
-.TH IP6TABLES 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
-.\"
-.\" Man page written by Andras Kis-Szabo <kisza@sch.bme.hu>
-.\" It is based on iptables man page.
-.\"
-.\" iptables page by Herve Eychenne <rv@wallfire.org>
-.\" It is based on ipchains man page.
-.\"
-.\" ipchains page by Paul ``Rusty'' Russell March 1997
-.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl>
-.\"
-.\" 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.
-.\"
-.\"
-.SH NAME
-ip6tables \(em IPv6 packet filter administration
-.SH SYNOPSIS
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] {\fB\-A\fP|\fB\-C\fP|\fB\-D\fP}
-\fIchain rule-specification\fP [\fIoptions...\fP]
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] \fB\-I\fP \fIchain\fP [\fIrulenum\fP]
-\fIrule-specification\fP [\fIoptions...\fP]
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] \fB\-R\fP \fIchain rulenum
-rule-specification\fP [\fIoptions...\fP]
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] \fB\-D\fP \fIchain rulenum\fP
-[\fIoptions...\fP]
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] \fB\-S\fP [\fIchain\fP [\fIrulenum\fP]]
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] {\fB\-F\fP|\fB\-L\fP|\fB\-Z\fP}
-[\fIchain\fP [\fIrulenum\fP]] [\fIoptions...\fP]
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] \fB\-N\fP \fIchain\fP
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] \fB\-X\fP [\fIchain\fP]
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] \fB\-P\fP \fIchain target\fP
-[\fIoptions...\fP]
-.PP
-\fBip6tables\fP [\fB\-t\fP \fItable\fP] \fB\-E\fP \fIold-chain-name new-chain-name\fP
-.SH DESCRIPTION
-\fBIp6tables\fP is used to set up, maintain, and inspect the
-tables of IPv6 packet
-filter rules in the Linux kernel. Several different tables
-may be defined. Each table contains a number of built-in
-chains and may also contain user-defined chains.
-.PP
-Each chain is a list of rules which can match a set of packets. Each
-rule specifies what to do with a packet that matches. This is called
-a `target', which may be a jump to a user-defined chain in the same
-table.
-.SH TARGETS
-A firewall rule specifies criteria for a packet and a target. If the
-packet does not match, the next rule in the chain is the examined; if
-it does match, then the next rule is specified by the value of the
-target, which can be the name of a user-defined chain or one of the
-special values \fBACCEPT\fP, \fBDROP\fP, \fBQUEUE\fP or \fBRETURN\fP.
-.PP
-\fBACCEPT\fP means to let the packet through.
-\fBDROP\fP means to drop the packet on the floor.
-\fBQUEUE\fP means to pass the packet to userspace.
-(How the packet can be received
-by a userspace process differs by the particular queue handler. 2.4.x
-and 2.6.x kernels up to 2.6.13 include the \fBip_queue\fP
-queue handler. Kernels 2.6.14 and later additionally include the
-\fBnfnetlink_queue\fP queue handler. Packets with a target of QUEUE will be
-sent to queue number '0' in this case. Please also see the \fBNFQUEUE\fP
-target as described later in this man page.)
-\fBRETURN\fP means stop traversing this chain and resume at the next
-rule in the
-previous (calling) chain. If the end of a built-in chain is reached
-or a rule in a built-in chain with target \fBRETURN\fP
-is matched, the target specified by the chain policy determines the
-fate of the packet.
-.SH TABLES
-There are currently five independent tables (which tables are present
-at any time depends on the kernel configuration options and which
-modules are present).
-.TP
-\fB\-t\fP, \fB\-\-table\fP \fItable\fP
-This option specifies the packet matching table which the command
-should operate on. If the kernel is configured with automatic module
-loading, an attempt will be made to load the appropriate module for
-that table if it is not already there.
-
-The tables are as follows:
-.RS
-.TP .4i
-\fBfilter\fP:
-This is the default table (if no \-t option is passed). It contains
-the built-in chains \fBINPUT\fP (for packets destined to local sockets),
-\fBFORWARD\fP (for packets being routed through the box), and
-\fBOUTPUT\fP (for locally-generated packets).
-.TP
-\fBnat\fP:
-This table is consulted when a packet that creates a new
-connection is encountered. It consists of three built-ins: \fBPREROUTING\fP
-(for altering packets as soon as they come in), \fBOUTPUT\fP
-(for altering locally-generated packets before routing), and \fBPOSTROUTING\fP
-(for altering packets as they are about to go out). Available since kernel 3.7.
-.TP
-\fBmangle\fP:
-This table is used for specialized packet alteration. Until kernel
-2.4.17 it had two built-in chains: \fBPREROUTING\fP
-(for altering incoming packets before routing) and \fBOUTPUT\fP
-(for altering locally-generated packets before routing).
-Since kernel 2.4.18, three other built-in chains are also supported:
-\fBINPUT\fP (for packets coming into the box itself), \fBFORWARD\fP
-(for altering packets being routed through the box), and \fBPOSTROUTING\fP
-(for altering packets as they are about to go out).
-.TP
-\fBraw\fP:
-This table is used mainly for configuring exemptions from connection
-tracking in combination with the NOTRACK target. It registers at the netfilter
-hooks with higher priority and is thus called before ip_conntrack, or any other
-IP tables. It provides the following built-in chains: \fBPREROUTING\fP
-(for packets arriving via any network interface) \fBOUTPUT\fP
-(for packets generated by local processes)
-.TP
-\fBsecurity\fP:
-This table is used for Mandatory Access Control (MAC) networking rules, such
-as those enabled by the \fBSECMARK\fP and \fBCONNSECMARK\fP targets.
-Mandatory Access Control is implemented by Linux Security Modules such as
-SELinux. The security table is called after the filter table, allowing any
-Discretionary Access Control (DAC) rules in the filter table to take effect
-before MAC rules. This table provides the following built-in chains:
-\fBINPUT\fP (for packets coming into the box itself),
-\fBOUTPUT\fP (for altering locally-generated packets before routing), and
-\fBFORWARD\fP (for altering packets being routed through the box).
-.RE
-.SH OPTIONS
-The options that are recognized by
-\fBip6tables\fP can be divided into several different groups.
-.SS COMMANDS
-These options specify the specific action to perform. Only one of them
-can be specified on the command line unless otherwise specified
-below. For all the long versions of the command and option names, you
-need to use only enough letters to ensure that
-\fBip6tables\fP can differentiate it from all other options.
-.TP
-\fB\-A\fP, \fB\-\-append\fP \fIchain rule-specification\fP
-Append one or more rules to the end of the selected chain.
-When the source and/or destination names resolve to more than one
-address, a rule will be added for each possible address combination.
-.TP
-\fB\-C\fP, \fB\-\-check\fP \fIchain rule-specification\fP
-Check whether a rule matching the specification does exist in the
-selected chain. This command uses the same logic as \fB\-D\fP to
-find a matching entry, but does not alter the existing iptables
-configuration and uses its exit code to indicate success or failure.
-.TP
-\fB\-D\fP, \fB\-\-delete\fP \fIchain rule-specification\fP
-.ns
-.TP
-\fB\-D\fP, \fB\-\-delete\fP \fIchain rulenum\fP
-Delete one or more rules from the selected chain. There are two
-versions of this command: the rule can be specified as a number in the
-chain (starting at 1 for the first rule) or a rule to match.
-.TP
-\fB\-I\fP, \fB\-\-insert\fP \fIchain\fP [\fIrulenum\fP] \fIrule-specification\fP
-Insert one or more rules in the selected chain as the given rule
-number. So, if the rule number is 1, the rule or rules are inserted
-at the head of the chain. This is also the default if no rule number
-is specified.
-.TP
-\fB\-R\fP, \fB\-\-replace\fP \fIchain rulenum rule-specification\fP
-Replace a rule in the selected chain. If the source and/or
-destination names resolve to multiple addresses, the command will
-fail. Rules are numbered starting at 1.
-.TP
-\fB\-L\fP, \fB\-\-list\fP [\fIchain\fP]
-List all rules in the selected chain. If no chain is selected, all
-chains are listed. Like every other ip6tables command, it applies to the
-specified table (filter is the default).
-.IP ""
-Please note that it is often used with the \fB\-n\fP
-option, in order to avoid long reverse DNS lookups.
-It is legal to specify the \fB\-Z\fP
-(zero) option as well, in which case the chain(s) will be atomically
-listed and zeroed. The exact output is affected by the other
-arguments given. The exact rules are suppressed until you use
-.nf
- ip6tables \-L \-v
-.fi
-.TP
-\fB\-S\fP, \fB\-\-list\-rules\fP [\fIchain\fP]
-Print all rules in the selected chain. If no chain is selected, all
-chains are printed like ip6tables-save. Like every other ip6tables command,
-it applies to the specified table (filter is the default).
-.TP
-\fB\-F\fP, \fB\-\-flush\fP [\fIchain\fP]
-Flush the selected chain (all the chains in the table if none is given).
-This is equivalent to deleting all the rules one by one.
-.TP
-\fB\-Z\fP, \fB\-\-zero\fP [\fIchain\fP [\fIrulenum\fP]]
-Zero the packet and byte counters in all chains, or only the given chain,
-or only the given rule in a chain. It is legal to
-specify the
-\fB\-L\fP, \fB\-\-list\fP
-(list) option as well, to see the counters immediately before they are
-cleared. (See above.)
-.TP
-\fB\-N\fP, \fB\-\-new\-chain\fP \fIchain\fP
-Create a new user-defined chain by the given name. There must be no
-target of that name already.
-.TP
-\fB\-X\fP, \fB\-\-delete\-chain\fP [\fIchain\fP]
-Delete the optional user-defined chain specified. There must be no references
-to the chain. If there are, you must delete or replace the referring rules
-before the chain can be deleted. The chain must be empty, i.e. not contain
-any rules. If no argument is given, it will attempt to delete every
-non-builtin chain in the table.
-.TP
-\fB\-P\fP, \fB\-\-policy\fP \fIchain target\fP
-Set the policy for the chain to the given target. See the section \fBTARGETS\fP
-for the legal targets. Only built-in (non-user-defined) chains can have
-policies, and neither built-in nor user-defined chains can be policy
-targets.
-.TP
-\fB\-E\fP, \fB\-\-rename\-chain\fP \fIold\-chain new\-chain\fP
-Rename the user specified chain to the user supplied name. This is
-cosmetic, and has no effect on the structure of the table.
-.TP
-\fB\-A\fP, \fB\-\-append\fP \fIchain rule-specification\fP
-Append one or more rules to the end of the selected chain.
-When the source and/or destination names resolve to more than one
-address, a rule will be added for each possible address combination.
-.TP
-\fB\-h\fP
-Help.
-Give a (currently very brief) description of the command syntax.
-.SS PARAMETERS
-The following parameters make up a rule specification (as used in the
-add, delete, insert, replace and append commands).
-.TP
-\fB\-4\fP, \fB\-\-ipv4\fP
-If a rule using the \fB\-4\fP option is inserted with (and only with)
-ip6tables-restore, it will be silently ignored. Any other uses will throw an
-error. This option allows to put both IPv4 and IPv6 rules in a single rule file
-for use with both iptables-restore and ip6tables-restore.
-.TP
-\fB\-6\fP, \fB\-\-ipv6\fP
-This option has no effect in ip6tables and ip6tables-restore.
-.TP
-[\fB!\fP] \fB\-p\fP, \fB\-\-protocol\fP \fIprotocol\fP
-The protocol of the rule or of the packet to check.
-The specified protocol can be one of \fBtcp\fP, \fBudp\fP, \fBudplite\fP,
-\fBicmpv6\fP, \fBesp\fP, \fBmh\fP or the special keyword "\fBall\fP",
-or it can be a numeric value, representing one of these protocols or a
-different one. A protocol name from /etc/protocols is also allowed.
-But IPv6 extension headers except \fBesp\fP are not allowed.
-\fBesp\fP and \fBipv6\-nonext\fP
-can be used with Kernel version 2.6.11 or later.
-A "!" argument before the protocol inverts the
-test. The number zero is equivalent to \fBall\fP, which means that you cannot
-test the protocol field for the value 0 directly. To match on a HBH header,
-even if it were the last, you cannot use \fB\-p 0\fP, but always need
-\fB\-m hbh\fP.
-"\fBall\fP"
-will match with all protocols and is taken as default when this
-option is omitted.
-.TP
-[\fB!\fP] \fB\-s\fP, \fB\-\-source\fP \fIaddress\fP[\fB/\fP\fImask\fP]
-Source specification.
-\fIAddress\fP can be either be a hostname,
-a network IP address (with \fB/\fP\fImask\fP), or a plain IP address.
-Names will be resolved once only, before the rule is submitted to the kernel.
-Please note that specifying any name to be resolved with a remote query such as
-DNS is a really bad idea.
-(Resolving network names is not supported at this time.)
-The \fImask\fP is a plain number,
-specifying the number of 1's at the left side of the network mask.
-A "!" argument before the address specification inverts the sense of
-the address. The flag \fB\-\-src\fP
-is an alias for this option.
-Multiple addresses can be specified, but this will \fBexpand to multiple
-rules\fP (when adding with \-A), or will cause multiple rules to be
-deleted (with \-D).
-.TP
-[\fB!\fP] \fB\-d\fP, \fB\-\-destination\fP \fIaddress\fP[\fB/\fP\fImask\fP]
-Destination specification.
-See the description of the \fB\-s\fP
-(source) flag for a detailed description of the syntax. The flag
-\fB\-\-dst\fP is an alias for this option.
-.TP
-\fB\-m\fP, \fB\-\-match\fP \fImatch\fP
-Specifies a match to use, that is, an extension module that tests for a
-specific property. The set of matches make up the condition under which a
-target is invoked. Matches are evaluated first to last as specified on the
-command line and work in short-circuit fashion, i.e. if one extension yields
-false, evaluation will stop.
-.TP
-\fB\-j\fP, \fB\-\-jump\fP \fItarget\fP
-This specifies the target of the rule; i.e., what to do if the packet
-matches it. The target can be a user-defined chain (other than the
-one this rule is in), one of the special builtin targets which decide
-the fate of the packet immediately, or an extension (see \fBEXTENSIONS\fP
-below). If this
-option is omitted in a rule (and \fB\-g\fP
-is not used), then matching the rule will have no
-effect on the packet's fate, but the counters on the rule will be
-incremented.
-.TP
-\fB\-g\fP, \fB\-\-goto\fP \fIchain\fP
-This specifies that the processing should continue in a user
-specified chain. Unlike the \-\-jump option return will not continue
-processing in this chain but instead in the chain that called us via
-\-\-jump.
-.TP
-[\fB!\fP] \fB\-i\fP, \fB\-\-in\-interface\fP \fIname\fP
-Name of an interface via which a packet was received (only for
-packets entering the \fBINPUT\fP, \fBFORWARD\fP and \fBPREROUTING\fP
-chains). When the "!" argument is used before the interface name, the
-sense is inverted. If the interface name ends in a "+", then any
-interface which begins with this name will match. If this option is
-omitted, any interface name will match.
-.TP
-[\fB!\fP] \fB\-o\fP, \fB\-\-out\-interface\fP \fIname\fP
-Name of an interface via which a packet is going to be sent (for packets
-entering the \fBFORWARD\fP, \fBOUTPUT\fP and \fBPOSTROUTING\fP
-chains). When the "!" argument is used before the interface name, the
-sense is inverted. If the interface name ends in a "+", then any
-interface which begins with this name will match. If this option is
-omitted, any interface name will match.
-.\" Currently not supported (header-based)
-.\" .TP
-.\" [\fB!\fP] \fB\-f\fP, \fB\-\-fragment\fP
-.\" This means that the rule only refers to second and further fragments
-.\" of fragmented packets. Since there is no way to tell the source or
-.\" destination ports of such a packet (or ICMP type), such a packet will
-.\" not match any rules which specify them. When the "!" argument
-.\" precedes the "\-f" flag, the rule will only match head fragments, or
-.\" unfragmented packets.
-.TP
-\fB\-c\fP, \fB\-\-set\-counters\fP \fIpackets bytes\fP
-This enables the administrator to initialize the packet and byte
-counters of a rule (during \fBINSERT\fP, \fBAPPEND\fP, \fBREPLACE\fP
-operations).
-.SS "OTHER OPTIONS"
-The following additional options can be specified:
-.TP
-\fB\-v\fP, \fB\-\-verbose\fP
-Verbose output. This option makes the list command show the interface
-name, the rule options (if any), and the TOS masks. The packet and
-byte counters are also listed, with the suffix 'K', 'M' or 'G' for
-1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
-the \fB\-x\fP flag to change this).
-For appending, insertion, deletion and replacement, this causes
-detailed information on the rule or rules to be printed. \fB\-v\fP may be
-specified multiple times to possibly emit more detailed debug statements.
-.TP
-\fB\-w\fP, \fB\-\-wait\fP
-Wait for the xtables lock.
-To prevent multiple instances of the program from running concurrently,
-an attempt will be made to obtain an exclusive lock at launch. By default,
-the program will exit if the lock cannot be obtained. This option will
-make the program wait until the exclusive lock can be obtained.
-.TP
-\fB\-n\fP, \fB\-\-numeric\fP
-Numeric output.
-IP addresses and port numbers will be printed in numeric format.
-By default, the program will try to display them as host names,
-network names, or services (whenever applicable).
-.TP
-\fB\-x\fP, \fB\-\-exact\fP
-Expand numbers.
-Display the exact value of the packet and byte counters,
-instead of only the rounded number in K's (multiples of 1000)
-M's (multiples of 1000K) or G's (multiples of 1000M). This option is
-only relevant for the \fB\-L\fP command.
-.TP
-\fB\-\-line\-numbers\fP
-When listing rules, add line numbers to the beginning of each rule,
-corresponding to that rule's position in the chain.
-.TP
-\fB\-\-modprobe=\fP\fIcommand\fP
-When adding or inserting rules into a chain, use \fIcommand\fP
-to load any necessary modules (targets, match extensions, etc).
-.SH MATCH EXTENSIONS
-.PP
-iptables can use extended packet matching and target modules.
-A list of these is available in the \fBiptables\-extensions\fP(8) manpage.
-.SH DIAGNOSTICS
-Various error messages are printed to standard error. The exit code
-is 0 for correct functioning. Errors which appear to be caused by
-invalid or abused command line parameters cause an exit code of 2, and
-other errors cause an exit code of 1.
-.SH BUGS
-Bugs? What's this? ;-)
-Well... the counters are not reliable on sparc64.
-.SH COMPATIBILITY WITH IPCHAINS
-This \fBip6tables\fP
-is very similar to ipchains by Rusty Russell. The main difference is
-that the chains \fBINPUT\fP and \fBOUTPUT\fP
-are only traversed for packets coming into the local host and
-originating from the local host respectively. Hence every packet only
-passes through one of the three chains (except loopback traffic, which
-involves both INPUT and OUTPUT chains); previously a forwarded packet
-would pass through all three.
-.PP
-The other main difference is that \fB\-i\fP refers to the input interface;
-\fB\-o\fP refers to the output interface, and both are available for packets
-entering the \fBFORWARD\fP chain.
-There are several other changes in ip6tables.
-.SH SEE ALSO
-\fBip6tables\-save\fP(8),
-\fBip6tables\-restore\fP(8),
-\fBiptables\fP(8),
-\fBiptables\-apply\fP(8),
-\fBiptables\-extensions\fP(8),
-\fBiptables\-save\fP(8),
-\fBiptables\-restore\fP(8),
-\fBlibipq\fP(3).
-.PP
-The packet-filtering-HOWTO details iptables usage for
-packet filtering,
-the netfilter-extensions-HOWTO details the extensions that are
-not in the standard distribution,
-and the netfilter-hacking-HOWTO details the netfilter internals.
-.br
-See
-.BR "http://www.netfilter.org/" .
-.SH AUTHORS
-Rusty Russell wrote iptables, in early consultation with Michael
-Neuling.
-.PP
-Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic packet
-selection framework in iptables, then wrote the mangle table, the owner match,
-the mark stuff, and ran around doing cool stuff everywhere.
-.PP
-James Morris wrote the TOS target, and tos match.
-.PP
-Jozsef Kadlecsik wrote the REJECT target.
-.PP
-Harald Welte wrote the ULOG and NFQUEUE target, the new libiptc, as well as TTL match+target and libipulog.
-.PP
-The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Yasuyuki Kozakai,
-Jozsef Kadlecsik, Patrick McHardy, James Morris, Pablo Neira Ayuso,
-Harald Welte and Rusty Russell.
-.PP
-ip6tables man page created by Andras Kis-Szabo, based on
-iptables man page written by Herve Eychenne <rv@wallfire.org>.
-.\" .. and did I mention that we are incredibly cool people?
-.\" .. sexy, too ..
-.\" .. witty, charming, powerful ..
-.\" .. and most of all, modest ..
-.SH VERSION
-.PP
-This manual page applies to ip6tables @PACKAGE_VERSION@.
diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
index 8eefb826..579d347b 100644
--- a/iptables/ip6tables.c
+++ b/iptables/ip6tables.c
@@ -76,6 +76,8 @@ static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
static const char optflags[]
= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c'};
+static const char unsupported_rev[] = " [unsupported revision]";
+
static struct option original_opts[] = {
{.name = "append", .has_arg = 1, .val = 'A'},
{.name = "delete", .has_arg = 1, .val = 'D'},
@@ -102,7 +104,8 @@ static struct option original_opts[] = {
{.name = "numeric", .has_arg = 0, .val = 'n'},
{.name = "out-interface", .has_arg = 1, .val = 'o'},
{.name = "verbose", .has_arg = 0, .val = 'v'},
- {.name = "wait", .has_arg = 0, .val = 'w'},
+ {.name = "wait", .has_arg = 2, .val = 'w'},
+ {.name = "wait-interval", .has_arg = 2, .val = 'W'},
{.name = "exact", .has_arg = 0, .val = 'x'},
{.name = "version", .has_arg = 0, .val = 'V'},
{.name = "help", .has_arg = 2, .val = 'h'},
@@ -121,6 +124,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
@@ -144,12 +148,12 @@ static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' ','x'},
/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
-/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' '},
/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*CHECK*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x'},
};
@@ -258,7 +262,10 @@ exit_printhelp(const struct xtables_rule_match *matches)
" network interface name ([+] for wildcard)\n"
" --table -t table table to manipulate (default: `filter')\n"
" --verbose -v verbose mode\n"
-" --wait -w wait for the xtables lock\n"
+" --wait -w [seconds] maximum wait to acquire xtables lock before give up\n"
+" --wait-interval -W [usecs] wait time to try to acquire xtables lock\n"
+" interval to wait for xtables lock\n"
+" default is 1 second\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"*/
@@ -387,6 +394,32 @@ parse_rulenumber(const char *rule)
return rulenum;
}
+static void
+parse_chain(const char *chainname)
+{
+ const char *ptr;
+
+ if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name `%s' too long (must be under %u chars)",
+ chainname, XT_EXTENSION_MAXNAMELEN);
+
+ if (*chainname == '-' || *chainname == '!')
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name not allowed to start "
+ "with `%c'\n", *chainname);
+
+ if (xtables_find_target(chainname, XTF_TRY_LOAD))
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name may not clash "
+ "with target name\n");
+
+ for (ptr = chainname; *ptr; ptr++)
+ if (isspace(*ptr))
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid chain name `%s'", chainname);
+}
+
static const char *
parse_target(const char *targetname)
{
@@ -489,8 +522,10 @@ print_match(const struct xt_entry_match *m,
xtables_find_match(m->u.user.name, XTF_TRY_LOAD, NULL);
if (match) {
- if (match->print)
+ if (match->print && m->u.user.revision == match->revision)
match->print(ip, m, numeric);
+ else if (match->print)
+ printf("%s%s ", match->name, unsupported_rev);
else
printf("%s ", match->name);
} else {
@@ -616,9 +651,11 @@ print_firewall(const struct ip6t_entry *fw,
IP6T_MATCH_ITERATE(fw, print_match, &fw->ipv6, format & FMT_NUMERIC);
if (target) {
- if (target->print)
+ if (target->print && t->u.user.revision == target->revision)
/* Print the target information. */
target->print(&fw->ipv6, t, format & FMT_NUMERIC);
+ else if (target->print)
+ printf(" %s%s", target->name, unsupported_rev);
} else if (t->u.target_size != sizeof(*t))
printf("[%u bytes of unknown target data] ",
(unsigned int)(t->u.target_size - sizeof(*t)));
@@ -1006,8 +1043,10 @@ static int print_match_save(const struct xt_entry_match *e,
match->alias ? match->alias(e) : e->u.user.name);
/* some matches don't provide a save function */
- if (match->save)
+ if (match->save && e->u.user.revision == match->revision)
match->save(ip, e);
+ else if (match->save)
+ printf(unsupported_rev);
} else {
if (e->u.match_size) {
fprintf(stderr,
@@ -1019,7 +1058,7 @@ static int print_match_save(const struct xt_entry_match *e,
return 0;
}
-/* print a given ip including mask if neccessary */
+/* Print a given ip including mask if necessary. */
static void print_ip(const char *prefix, const struct in6_addr *ip,
const struct in6_addr *mask, int invert)
{
@@ -1040,8 +1079,9 @@ static void print_ip(const char *prefix, const struct in6_addr *ip,
printf("/%d", l);
}
-/* We want this to be readable, so only print out neccessary fields.
- * Because that's the kind of world I want to live in. */
+/* We want this to be readable, so only print out necessary fields.
+ * Because that's the kind of world I want to live in.
+ */
void print_rule6(const struct ip6t_entry *e,
struct xtc_handle *h, const char *chain, int counters)
{
@@ -1106,8 +1146,10 @@ void print_rule6(const struct ip6t_entry *e,
}
printf(" -j %s", target->alias ? target->alias(t) : target_name);
- if (target->save)
+ if (target->save && t->u.user.revision == target->revision)
target->save(&e->ipv6, t);
+ else if (target->save)
+ printf(unsupported_rev);
else {
/* If the target size is greater than xt_entry_target
* there is something to be saved, we just don't know
@@ -1296,7 +1338,11 @@ int do_command6(int argc, char *argv[], char **table,
struct in6_addr *smasks = NULL, *dmasks = NULL;
int verbose = 0;
- bool wait = false;
+ int wait = 0;
+ struct timeval wait_interval = {
+ .tv_sec = 1,
+ };
+ bool wait_interval_set = false;
const char *chain = NULL;
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
const char *policy = NULL, *newname = NULL;
@@ -1332,7 +1378,7 @@ int do_command6(int argc, char *argv[], char **table,
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:bvwnt:m:xc:g:46",
+ "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvw::W::nt:m:xc:g:46",
opts, NULL)) != -1) {
switch (cs.c) {
/*
@@ -1354,8 +1400,7 @@ int do_command6(int argc, char *argv[], char **table,
add_command(&command, CMD_DELETE, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!') {
+ if (xs_has_arg(argc, argv)) {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_DELETE_NUM;
}
@@ -1365,8 +1410,7 @@ int do_command6(int argc, char *argv[], char **table,
add_command(&command, CMD_REPLACE, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
else
xtables_error(PARAMETER_PROBLEM,
@@ -1378,8 +1422,7 @@ int do_command6(int argc, char *argv[], char **table,
add_command(&command, CMD_INSERT, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
else rulenum = 1;
break;
@@ -1388,11 +1431,9 @@ int do_command6(int argc, char *argv[], char **table,
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] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
break;
@@ -1400,11 +1441,9 @@ int do_command6(int argc, char *argv[], char **table,
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] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
break;
@@ -1412,8 +1451,7 @@ int do_command6(int argc, char *argv[], char **table,
add_command(&command, CMD_FLUSH, CMD_NONE,
cs.invert);
if (optarg) chain = optarg;
- else if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
@@ -1421,25 +1459,16 @@ int do_command6(int argc, char *argv[], char **table,
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] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!') {
+ if (xs_has_arg(argc, argv)) {
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");
+ parse_chain(optarg);
add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
@@ -1449,8 +1478,7 @@ int do_command6(int argc, char *argv[], char **table,
add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
cs.invert);
if (optarg) chain = optarg;
- else if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
@@ -1458,8 +1486,7 @@ int do_command6(int argc, char *argv[], char **table,
add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
newname = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
@@ -1472,8 +1499,7 @@ int do_command6(int argc, char *argv[], char **table,
add_command(&command, CMD_SET_POLICY, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
policy = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
@@ -1583,7 +1609,17 @@ int do_command6(int argc, char *argv[], char **table,
"You cannot use `-w' from "
"ip6tables-restore");
}
- wait = true;
+ wait = parse_wait_time(argc, argv);
+ break;
+
+ case 'W':
+ if (restore) {
+ xtables_error(PARAMETER_PROBLEM,
+ "You cannot use `-W' from "
+ "ip6tables-restore");
+ }
+ parse_wait_interval(argc, argv, &wait_interval);
+ wait_interval_set = true;
break;
case 'm':
@@ -1632,8 +1668,7 @@ int do_command6(int argc, char *argv[], char **table,
bcnt = strchr(pcnt + 1, ',');
if (bcnt)
bcnt++;
- if (!bcnt && optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (!bcnt && xs_has_arg(argc, argv))
bcnt = argv[optind++];
if (!bcnt)
xtables_error(PARAMETER_PROBLEM,
@@ -1690,6 +1725,17 @@ int do_command6(int argc, char *argv[], char **table,
cs.invert = FALSE;
}
+ if (!wait && wait_interval_set)
+ xtables_error(PARAMETER_PROBLEM,
+ "--wait-interval only makes sense with --wait\n");
+
+ 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)
@@ -1732,15 +1778,13 @@ int do_command6(int argc, char *argv[], char **table,
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);
-
/* Attempt to acquire the xtables lock */
- if (!restore && (xtables_lock(wait) == XT_LOCK_BUSY)) {
- fprintf(stderr, "Another app is currently holding the xtables lock. "
- "Perhaps you want to use the -w option?\n");
+ if (!restore && xtables_lock(wait, &wait_interval) == XT_LOCK_BUSY) {
+ fprintf(stderr, "Another app is currently holding the xtables lock. ");
+ if (wait == 0)
+ fprintf(stderr, "Perhaps you want to use the -w option?\n");
+ else
+ fprintf(stderr, "Stopped waiting after %ds.\n", wait);
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
@@ -1813,10 +1857,11 @@ int do_command6(int argc, char *argv[], char **table,
}
if (!cs.target) {
- /* it is no chain, and we can't load a plugin.
+ /* It is no chain, and we can't load a plugin.
* We cannot know if the plugin is corrupt, non
- * existant OR if the user just misspelled a
- * chain. */
+ * existent OR if the user just misspelled a
+ * chain.
+ */
#ifdef IP6T_F_GOTO
if (cs.fw6.ipv6.flags & IP6T_F_GOTO)
xtables_error(PARAMETER_PROBLEM,
diff --git a/iptables/iptables-apply.8 b/iptables/iptables-apply.8.in
index 66eaf57a..cdc9c447 100644
--- a/iptables/iptables-apply.8
+++ b/iptables/iptables-apply.8.in
@@ -2,7 +2,7 @@
.\" Author: Martin F. Krafft
.\" Date: Jun 04, 2006
.\"
-.TH iptables\-apply 8 2006-06-04
+.TH IPTABLES\-APPLY 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
.\" disable hyphenation
.nh
.SH NAME
diff --git a/iptables/iptables-restore.8 b/iptables/iptables-restore.8.in
index 2b1d102c..7a286b91 100644
--- a/iptables/iptables-restore.8
+++ b/iptables/iptables-restore.8.in
@@ -1,4 +1,4 @@
-.TH IPTABLES-RESTORE 8 "Jan 04, 2001" "" ""
+.TH IPTABLES-RESTORE 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
.\"
.\" Man page written by Harald Welte <laforge@gnumonks.org>
.\" It is based on the iptables man page.
@@ -20,14 +20,22 @@
.\"
.SH NAME
iptables-restore \(em Restore IP Tables
+.P
+ip6tables-restore \(em Restore IPv6 Tables
.SH SYNOPSIS
\fBiptables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
-[\fB\-T\fP \fIname\fP]
+[\fB\-T\fP \fIname\fP] [\fBfile\fP]
+.P
+\fBip6tables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
+[\fB\-T\fP \fIname\fP] [\fBfile\fP]
.SH DESCRIPTION
.PP
.B iptables-restore
-is used to restore IP Tables from data specified on STDIN. Use
-I/O redirection provided by your shell to read from a file
+and
+.B ip6tables-restore
+are used to restore IP and IPv6 Tables from data specified on STDIN or in
+\fIfile\fP. Use I/O redirection provided by your shell to read from a file or
+specify \fIfile\fP as an argument.
.TP
\fB\-c\fR, \fB\-\-counters\fR
restore the values of all packet and byte counters
@@ -35,10 +43,9 @@ restore the values of all packet and byte counters
\fB\-h\fP, \fB\-\-help\fP
Print a short option summary.
.TP
-\fB\-n\fR, \fB\-\-noflush\fR
-don't flush the previous contents of the table. If not specified,
-.B iptables-restore
-flushes (deletes) all previous contents of the respective table.
+\fB\-n\fR, \fB\-\-noflush\fR
+don't flush the previous contents of the table. If not specified,
+both commands flush (delete) all previous contents of the respective table.
.TP
\fB\-t\fP, \fB\-\-test\fP
Only parse and construct the ruleset, but do not commit it.
@@ -54,8 +61,11 @@ inspect /proc/sys/kernel/modprobe to determine the executable's path.
Restore only the named table even if the input stream contains other ones.
.SH BUGS
None known as of iptables-1.2.1 release
-.SH AUTHOR
-Harald Welte <laforge@gnumonks.org>
+.SH AUTHORS
+Harald Welte <laforge@gnumonks.org> wrote iptables-restore based on code
+from Rusty Russell.
+.br
+Andras Kis-Szabo <kisza@sch.bme.hu> contributed ip6tables-restore.
.SH SEE ALSO
\fBiptables\-save\fP(8), \fBiptables\fP(8)
.PP
diff --git a/iptables/iptables-restore.c b/iptables/iptables-restore.c
index a41a46d3..113045df 100644
--- a/iptables/iptables-restore.c
+++ b/iptables/iptables-restore.c
@@ -6,7 +6,7 @@
*/
#include <getopt.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
@@ -23,19 +23,23 @@
#define DEBUGP(x, args...)
#endif
-static int binary = 0, counters = 0, verbose = 0, noflush = 0, wait = 0;
+static int counters = 0, verbose = 0, noflush = 0, wait = 0;
+
+static struct timeval wait_interval = {
+ .tv_sec = 1,
+};
/* 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 = "wait", .has_arg = false, .val = 'w'},
- {.name = "modprobe", .has_arg = true, .val = 'M'},
- {.name = "table", .has_arg = true, .val = 'T'},
+ {.name = "counters", .has_arg = 0, .val = 'c'},
+ {.name = "verbose", .has_arg = 0, .val = 'v'},
+ {.name = "test", .has_arg = 0, .val = 't'},
+ {.name = "help", .has_arg = 0, .val = 'h'},
+ {.name = "noflush", .has_arg = 0, .val = 'n'},
+ {.name = "modprobe", .has_arg = 1, .val = 'M'},
+ {.name = "table", .has_arg = 1, .val = 'T'},
+ {.name = "wait", .has_arg = 2, .val = 'w'},
+ {.name = "wait-interval", .has_arg = 2, .val = 'W'},
{NULL},
};
@@ -45,16 +49,16 @@ static void print_usage(const char *name, const char *version) __attribute__((no
static void print_usage(const char *name, const char *version)
{
- fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h] [-w]\n"
- " [ --binary ]\n"
+ fprintf(stderr, "Usage: %s [-c] [-v] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command]\n"
" [ --counters ]\n"
" [ --verbose ]\n"
" [ --test ]\n"
" [ --help ]\n"
" [ --noflush ]\n"
- " [ --wait ]\n"
+ " [ --wait=<seconds>\n"
+ " [ --wait-interval=<usecs>\n"
" [ --table=<TABLE> ]\n"
- " [ --modprobe=<command>]\n", name);
+ " [ --modprobe=<command> ]\n", name);
exit(1);
}
@@ -208,10 +212,10 @@ iptables_restore_main(int argc, char *argv[])
init_extensions4();
#endif
- while ((c = getopt_long(argc, argv, "bcvthnwM:T:", options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "bcvthnwWM:T:", options, NULL)) != -1) {
switch (c) {
case 'b':
- binary = 1;
+ fprintf(stderr, "-b/--binary option is not implemented\n");
break;
case 'c':
counters = 1;
@@ -230,7 +234,10 @@ iptables_restore_main(int argc, char *argv[])
noflush = 1;
break;
case 'w':
- wait = 1;
+ wait = parse_wait_time(argc, argv);
+ break;
+ case 'W':
+ parse_wait_interval(argc, argv, &wait_interval);
break;
case 'M':
xtables_modprobe_program = optarg;
@@ -279,14 +286,16 @@ iptables_restore_main(int argc, char *argv[])
ret = 1;
}
- /* Release the lock since we're done with the current table. */
+ /* Done with the current table, release the lock. */
if (lock >= 0) {
xtables_unlock(lock);
+ lock = XT_LOCK_NOT_ACQUIRED;
}
+
in_table = 0;
} else if ((buffer[0] == '*') && (!in_table)) {
/* Acquire a lock before we create a new table handle */
- lock = xtables_lock(wait);
+ lock = xtables_lock(wait, &wait_interval);
if (lock == XT_LOCK_BUSY) {
fprintf(stderr, "Another app is currently holding the xtables lock. "
"Perhaps you want to use the -w option?\n");
diff --git a/iptables/iptables-save.8 b/iptables/iptables-save.8.in
index c2e0a949..7f99d8a3 100644
--- a/iptables/iptables-save.8
+++ b/iptables/iptables-save.8.in
@@ -1,4 +1,4 @@
-.TH IPTABLES-SAVE 8 "Jan 04, 2001" "" ""
+.TH IPTABLES-SAVE 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
.\"
.\" Man page written by Harald Welte <laforge@gnumonks.org>
.\" It is based on the iptables man page.
@@ -20,16 +20,23 @@
.\"
.SH NAME
iptables-save \(em dump iptables rules to stdout
+.P
+ip6tables-save \(em dump iptables rules to stdout
.SH SYNOPSIS
\fBiptables\-save\fP [\fB\-M\fP \fImodprobe\fP] [\fB\-c\fP]
[\fB\-t\fP \fItable\fP]
+.P
+\fBip6tables\-save\fP [\fB\-M\fP \fImodprobe\fP] [\fB\-c\fP]
+[\fB\-t\fP \fItable\fP]
.SH DESCRIPTION
.PP
.B iptables-save
-is used to dump the contents of an IP Table in easily parseable format
+and
+.B ip6tables-save
+are used to dump the contents of IP or IPv6 Table in easily parseable format
to STDOUT. Use I/O-redirection provided by your shell to write to a file.
.TP
-\fB\-M\fP \fImodprobe_program\fP
+\fB\-M\fR, \fB\-\-modprobe\fR \fImodprobe_program\fP
Specify the path to the modprobe program. By default, iptables-save will
inspect /proc/sys/kernel/modprobe to determine the executable's path.
.TP
@@ -41,8 +48,12 @@ restrict output to only one table. If not specified, output includes all
available tables.
.SH BUGS
None known as of iptables-1.2.1 release
-.SH AUTHOR
+.SH AUTHORS
Harald Welte <laforge@gnumonks.org>
+.br
+Rusty Russell <rusty@rustcorp.com.au>
+.br
+Andras Kis-Szabo <kisza@sch.bme.hu> contributed ip6tables-save.
.SH SEE ALSO
\fBiptables\-restore\fP(8), \fBiptables\fP(8)
.PP
diff --git a/iptables/iptables-save.c b/iptables/iptables-save.c
index e599fcec..238f368e 100644
--- a/iptables/iptables-save.c
+++ b/iptables/iptables-save.c
@@ -6,7 +6,7 @@
*
*/
#include <getopt.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -17,10 +17,6 @@
#include "iptables.h"
#include "iptables-multi.h"
-#ifndef NO_SHARED_LIBS
-#include <dlfcn.h>
-#endif
-
static int show_counters = 0;
static const struct option options[] = {
@@ -140,8 +136,11 @@ iptables_save_main(int argc, char *argv[])
init_extensions4();
#endif
- while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "bcdt:M:", options, NULL)) != -1) {
switch (c) {
+ case 'b':
+ fprintf(stderr, "-b/--binary option is not implemented\n");
+ break;
case 'c':
show_counters = 1;
break;
diff --git a/iptables/iptables-xml.1 b/iptables/iptables-xml.1.in
index 048c2cb8..7b7878f8 100644
--- a/iptables/iptables-xml.1
+++ b/iptables/iptables-xml.1.in
@@ -1,4 +1,4 @@
-.TH IPTABLES-XML 8 "Jul 16, 2007" "" ""
+.TH IPTABLES-XML 1 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
.\"
.\" Man page written by Sam Liddicott <azez@ufomechanic.net>
.\" It is based on the iptables-save man page.
diff --git a/iptables/iptables-xml.c b/iptables/iptables-xml.c
index 96284476..740a563c 100644
--- a/iptables/iptables-xml.c
+++ b/iptables/iptables-xml.c
@@ -7,7 +7,7 @@
*/
#include <getopt.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -131,8 +131,9 @@ free_argv(void)
oldargc = 0;
}
-/* save parsed rule for comparison with next rule
- to perform action agregation on duplicate conditions */
+/* Save parsed rule for comparison with next rule to perform action aggregation
+ * on duplicate conditions.
+ */
static void
save_argv(void)
{
@@ -527,12 +528,13 @@ do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
static int
compareRules(void)
{
- /* compare arguments up to -j or -g for match.
- NOTE: We don't want to combine actions if there were no criteria
- in each rule, or rules didn't have an action
- NOTE: Depends on arguments being in some kind of "normal" order which
- is the case when processing the ACTUAL output of actual iptables-save
- rather than a file merely in a compatable format */
+ /* Compare arguments up to -j or -g for match.
+ * NOTE: We don't want to combine actions if there were no criteria
+ * in each rule, or rules didn't have an action.
+ * NOTE: Depends on arguments being in some kind of "normal" order which
+ * is the case when processing the ACTUAL output of actual iptables-save
+ * rather than a file merely in a compatible format.
+ */
unsigned int old = 0;
unsigned int new = 0;
@@ -845,6 +847,11 @@ iptables_xml_main(int argc, char *argv[])
for (a = 0; a < newargc; a++)
DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+ if (!chain) {
+ fprintf(stderr, "%s: line %u failed - no chain found\n",
+ prog_name, line);
+ exit(1);
+ }
needChain(chain);// Should we explicitly look for -A
do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
diff --git a/iptables/iptables.8.in b/iptables/iptables.8.in
index 6f310039..a9c6b252 100644
--- a/iptables/iptables.8.in
+++ b/iptables/iptables.8.in
@@ -23,10 +23,13 @@
.\"
.\"
.SH NAME
-iptables \(em administration tool for IPv4 packet filtering and NAT
+iptables/ip6tables \(em administration tool for IPv4/IPv6 packet filtering and NAT
.SH SYNOPSIS
\fBiptables\fP [\fB\-t\fP \fItable\fP] {\fB\-A\fP|\fB\-C\fP|\fB\-D\fP}
\fIchain\fP \fIrule-specification\fP
+.P
+\fBip6tables\fP [\fB\-t\fP \fItable\fP] {\fB\-A\fP|\fB\-C\fP|\fB\-D\fP}
+\fIchain rule-specification\fP
.PP
\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-I\fP \fIchain\fP [\fIrulenum\fP] \fIrule-specification\fP
.PP
@@ -52,8 +55,8 @@ match = \fB\-m\fP \fImatchname\fP [\fIper-match-options\fP]
.PP
target = \fB\-j\fP \fItargetname\fP [\fIper\-target\-options\fP]
.SH DESCRIPTION
-\fBIptables\fP is used to set up, maintain, and inspect the
-tables of IPv4 packet
+\fBIptables\fP and \fBip6tables\fP are used to set up, maintain, and inspect the
+tables of IPv4 and IPv6 packet
filter rules in the Linux kernel. Several different tables
may be defined. Each table contains a number of built-in
chains and may also contain user-defined chains.
@@ -64,21 +67,14 @@ a `target', which may be a jump to a user-defined chain in the same
table.
.SH TARGETS
A firewall rule specifies criteria for a packet and a target. If the
-packet does not match, the next rule in the chain is the examined; if
+packet does not match, the next rule in the chain is examined; if
it does match, then the next rule is specified by the value of the
-target, which can be the name of a user-defined chain or one of the
-special values \fBACCEPT\fP, \fBDROP\fP, \fBQUEUE\fP or \fBRETURN\fP.
+target, which can be the name of a user-defined chain, one of the targets
+described in \fBiptables\-extensions\fP(8), or one of the
+special values \fBACCEPT\fP, \fBDROP\fP or \fBRETURN\fP.
.PP
\fBACCEPT\fP means to let the packet through.
\fBDROP\fP means to drop the packet on the floor.
-\fBQUEUE\fP means to pass the packet to userspace.
-(How the packet can be received
-by a userspace process differs by the particular queue handler. 2.4.x
-and 2.6.x kernels up to 2.6.13 include the \fBip_queue\fP
-queue handler. Kernels 2.6.14 and later additionally include the
-\fBnfnetlink_queue\fP queue handler. Packets with a target of QUEUE will be
-sent to queue number '0' in this case. Please also see the \fBNFQUEUE\fP
-target as described later in this man page.)
\fBRETURN\fP means stop traversing this chain and resume at the next
rule in the
previous (calling) chain. If the end of a built-in chain is reached
@@ -107,10 +103,12 @@ the built-in chains \fBINPUT\fP (for packets destined to local sockets),
.TP
\fBnat\fP:
This table is consulted when a packet that creates a new
-connection is encountered. It consists of three built-ins: \fBPREROUTING\fP
-(for altering packets as soon as they come in), \fBOUTPUT\fP
+connection is encountered. It consists of four built-ins: \fBPREROUTING\fP
+(for altering packets as soon as they come in), \fBINPUT\fP (for altering
+packets destined for local sockets), \fBOUTPUT\fP
(for altering locally-generated packets before routing), and \fBPOSTROUTING\fP
(for altering packets as they are about to go out).
+IPv6 NAT support is available since kernel 3.7.
.TP
\fBmangle\fP:
This table is used for specialized packet alteration. Until kernel
@@ -143,7 +141,7 @@ before MAC rules. This table provides the following built-in chains:
.RE
.SH OPTIONS
The options that are recognized by
-\fBiptables\fP can be divided into several different groups.
+\fBiptables\fP and \fBip6tables\fP can be divided into several different groups.
.SS COMMANDS
These options specify the desired action to perform. Only one of them
can be specified on the command line unless otherwise stated
@@ -197,6 +195,8 @@ arguments given. The exact rules are suppressed until you use
.nf
iptables \-L \-v
.fi
+or
+\fBiptables\-save\fP(8).
.TP
\fB\-S\fP, \fB\-\-list\-rules\fP [\fIchain\fP]
Print all rules in the selected chain. If no chain is selected, all
@@ -227,10 +227,8 @@ any rules. If no argument is given, it will attempt to delete every
non-builtin chain in the table.
.TP
\fB\-P\fP, \fB\-\-policy\fP \fIchain target\fP
-Set the policy for the chain to the given target. See the section \fBTARGETS\fP
-for the legal targets. Only built-in (non-user-defined) chains can have
-policies, and neither built-in nor user-defined chains can be policy
-targets.
+Set the policy for the built-in (non-user-defined) chain to the given target.
+The policy target must be either \fBACCEPT\fP or \fBDROP\fP.
.TP
\fB\-E\fP, \fB\-\-rename\-chain\fP \fIold\-chain new\-chain\fP
Rename the user specified chain to the user supplied name. This is
@@ -245,23 +243,35 @@ add, delete, insert, replace and append commands).
.TP
\fB\-4\fP, \fB\-\-ipv4\fP
This option has no effect in iptables and iptables-restore.
+If a rule using the \fB\-4\fP option is inserted with (and only with)
+ip6tables-restore, it will be silently ignored. Any other uses will throw an
+error. This option allows to put both IPv4 and IPv6 rules in a single rule file
+for use with both iptables-restore and ip6tables-restore.
.TP
\fB\-6\fP, \fB\-\-ipv6\fP
If a rule using the \fB\-6\fP option is inserted with (and only with)
iptables-restore, it will be silently ignored. Any other uses will throw an
error. This option allows to put both IPv4 and IPv6 rules in a single rule file
for use with both iptables-restore and ip6tables-restore.
+This option has no effect in ip6tables and ip6tables-restore.
.TP
[\fB!\fP] \fB\-p\fP, \fB\-\-protocol\fP \fIprotocol\fP
The protocol of the rule or of the packet to check.
The specified protocol can be one of \fBtcp\fP, \fBudp\fP, \fBudplite\fP,
-\fBicmp\fP, \fBesp\fP, \fBah\fP, \fBsctp\fP or the special keyword "\fBall\fP",
+\fBicmp\fP, \fBicmpv6\fP,\fBesp\fP, \fBah\fP, \fBsctp\fP, \fBmh\fP or the special keyword "\fBall\fP",
or it can be a numeric value, representing one of these protocols or a
different one. A protocol name from /etc/protocols is also allowed.
A "!" argument before the protocol inverts the
test. The number zero is equivalent to \fBall\fP. "\fBall\fP"
will match with all protocols and is taken as default when this
option is omitted.
+Note that, in ip6tables, IPv6 extension headers except \fBesp\fP are not allowed.
+\fBesp\fP and \fBipv6\-nonext\fP
+can be used with Kernel version 2.6.11 or later.
+The number zero is equivalent to \fBall\fP, which means that you cannot
+test the protocol field for the value 0 directly. To match on a HBH header,
+even if it were the last, you cannot use \fB\-p 0\fP, but always need
+\fB\-m hbh\fP.
.TP
[\fB!\fP] \fB\-s\fP, \fB\-\-source\fP \fIaddress\fP[\fB/\fP\fImask\fP][\fB,\fP\fI...\fP]
Source specification. \fIAddress\fP
@@ -271,9 +281,9 @@ be resolved once only, before the rule is submitted to the kernel.
Please note that specifying any name to be resolved with a remote query such as
DNS is a really bad idea.
The \fImask\fP
-can be either a network mask or a plain number,
+can be either an ipv4 network mask (for iptables) or a plain number,
specifying the number of 1's at the left side of the network mask.
-Thus, a mask of \fI24\fP is equivalent to \fI255.255.255.0\fP.
+Thus, an iptables mask of \fI24\fP is equivalent to \fI255.255.255.0\fP.
A "!" argument before the address specification inverts the sense of
the address. The flag \fB\-\-src\fP is an alias for this option.
Multiple addresses can be specified, but this will \fBexpand to multiple
@@ -327,12 +337,13 @@ interface which begins with this name will match. If this option is
omitted, any interface name will match.
.TP
[\fB!\fP] \fB\-f\fP, \fB\-\-fragment\fP
-This means that the rule only refers to second and further fragments
+This means that the rule only refers to second and further IPv4 fragments
of fragmented packets. Since there is no way to tell the source or
destination ports of such a packet (or ICMP type), such a packet will
not match any rules which specify them. When the "!" argument
precedes the "\-f" flag, the rule will only match head fragments, or
-unfragmented packets.
+unfragmented packets. This option is IPv4 specific, it is not available
+in ip6tables.
.TP
\fB\-c\fP, \fB\-\-set\-counters\fP \fIpackets bytes\fP
This enables the administrator to initialize the packet and byte
@@ -351,12 +362,20 @@ For appending, insertion, deletion and replacement, this causes
detailed information on the rule or rules to be printed. \fB\-v\fP may be
specified multiple times to possibly emit more detailed debug statements.
.TP
-\fB\-w\fP, \fB\-\-wait\fP
+\fB\-w\fP, \fB\-\-wait\fP [\fIseconds\fP]
Wait for the xtables lock.
To prevent multiple instances of the program from running concurrently,
an attempt will be made to obtain an exclusive lock at launch. By default,
the program will exit if the lock cannot be obtained. This option will
-make the program wait until the exclusive lock can be obtained.
+make the program wait (indefinitely or for optional \fIseconds\fP) until
+the exclusive lock can be obtained.
+.TP
+\fB\-W\fP, \fB\-\-wait-interval\fP \fImicroseconds\fP
+Interval to wait per each iteration.
+When running latency sensitive applications, waiting for the xtables lock
+for extended durations may not be acceptable. This option will make each
+iteration take the amount of time specified. The default interval is
+1 second. This option only works with \fB\-w\fP.
.TP
\fB\-n\fP, \fB\-\-numeric\fP
Numeric output.
@@ -420,10 +439,6 @@ There are several other changes in iptables.
\fBiptables\-save\fP(8),
\fBiptables\-restore\fP(8),
\fBiptables\-extensions\fP(8),
-\fBip6tables\fP(8),
-\fBip6tables\-save\fP(8),
-\fBip6tables\-restore\fP(8),
-\fBlibipq\fP(3).
.PP
The packet-filtering-HOWTO details iptables usage for
packet filtering, the NAT-HOWTO details NAT,
@@ -447,9 +462,10 @@ Jozsef Kadlecsik wrote the REJECT target.
.PP
Harald Welte wrote the ULOG and NFQUEUE target, the new libiptc, as well as the TTL, DSCP, ECN matches and targets.
.PP
-The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Yasuyuki Kozakai,
-Jozsef Kadlecsik, Patrick McHardy, James Morris, Pablo Neira Ayuso,
-Harald Welte and Rusty Russell.
+The Netfilter Core Team is: Jozsef Kadlecsik, Patrick McHardy, Pablo Neira
+Ayuso, Eric Leblond and Florian Westphal. Emeritus Core Team members are: Marc
+Boucher, Martin Josefsson, Yasuyuki Kozakai, James Morris, Harald Welte and
+Rusty Russell.
.PP
Man page originally written by Herve Eychenne <rv@wallfire.org>.
.\" .. and did I mention that we are incredibly cool people?
@@ -458,4 +474,4 @@ Man page originally written by Herve Eychenne <rv@wallfire.org>.
.\" .. and most of all, modest ..
.SH VERSION
.PP
-This manual page applies to iptables @PACKAGE_VERSION@.
+This manual page applies to iptables/ip6tables @PACKAGE_VERSION@.
diff --git a/iptables/iptables.c b/iptables/iptables.c
index 1ace4cc3..62731c5e 100644
--- a/iptables/iptables.c
+++ b/iptables/iptables.c
@@ -73,6 +73,8 @@ static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
static const char optflags[]
= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f'};
+static const char unsupported_rev[] = " [unsupported revision]";
+
static struct option original_opts[] = {
{.name = "append", .has_arg = 1, .val = 'A'},
{.name = "delete", .has_arg = 1, .val = 'D'},
@@ -99,7 +101,8 @@ static struct option original_opts[] = {
{.name = "numeric", .has_arg = 0, .val = 'n'},
{.name = "out-interface", .has_arg = 1, .val = 'o'},
{.name = "verbose", .has_arg = 0, .val = 'v'},
- {.name = "wait", .has_arg = 0, .val = 'w'},
+ {.name = "wait", .has_arg = 2, .val = 'w'},
+ {.name = "wait-interval", .has_arg = 2, .val = 'W'},
{.name = "exact", .has_arg = 0, .val = 'x'},
{.name = "fragments", .has_arg = 0, .val = 'f'},
{.name = "version", .has_arg = 0, .val = 'V'},
@@ -120,6 +123,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
@@ -143,12 +147,12 @@ static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/*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'},
+/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*CHECK*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
};
@@ -252,7 +256,9 @@ exit_printhelp(const struct xtables_rule_match *matches)
" network interface name ([+] for wildcard)\n"
" --table -t table table to manipulate (default: `filter')\n"
" --verbose -v verbose mode\n"
-" --wait -w wait for the xtables lock\n"
+" --wait -w [seconds] maximum wait to acquire xtables lock before give up\n"
+" --wait-interval -W [usecs] wait time to try to acquire xtables lock\n"
+" default is 1 second\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"
@@ -373,6 +379,32 @@ parse_rulenumber(const char *rule)
return rulenum;
}
+static void
+parse_chain(const char *chainname)
+{
+ const char *ptr;
+
+ if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name `%s' too long (must be under %u chars)",
+ chainname, XT_EXTENSION_MAXNAMELEN);
+
+ if (*chainname == '-' || *chainname == '!')
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name not allowed to start "
+ "with `%c'\n", *chainname);
+
+ if (xtables_find_target(chainname, XTF_TRY_LOAD))
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name may not clash "
+ "with target name\n");
+
+ for (ptr = chainname; *ptr; ptr++)
+ if (isspace(*ptr))
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid chain name `%s'", chainname);
+}
+
static const char *
parse_target(const char *targetname)
{
@@ -474,8 +506,10 @@ print_match(const struct xt_entry_match *m,
xtables_find_match(m->u.user.name, XTF_TRY_LOAD, NULL);
if (match) {
- if (match->print)
+ if (match->print && m->u.user.revision == match->revision)
match->print(ip, m, numeric);
+ else if (match->print)
+ printf("%s%s ", match->name, unsupported_rev);
else
printf("%s ", match->name);
} else {
@@ -601,9 +635,11 @@ print_firewall(const struct ipt_entry *fw,
IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
if (target) {
- if (target->print)
+ if (target->print && t->u.user.revision == target->revision)
/* Print the target information. */
target->print(&fw->ip, t, format & FMT_NUMERIC);
+ else if (target->print)
+ printf(" %s%s", target->name, unsupported_rev);
} else if (t->u.target_size != sizeof(*t))
printf("[%u bytes of unknown target data] ",
(unsigned int)(t->u.target_size - sizeof(*t)));
@@ -997,8 +1033,10 @@ static int print_match_save(const struct xt_entry_match *e,
match->alias ? match->alias(e) : e->u.user.name);
/* some matches don't provide a save function */
- if (match->save)
+ if (match->save && e->u.user.revision == match->revision)
match->save(ip, e);
+ else if (match->save)
+ printf(unsupported_rev);
} else {
if (e->u.match_size) {
fprintf(stderr,
@@ -1010,7 +1048,7 @@ static int print_match_save(const struct xt_entry_match *e,
return 0;
}
-/* print a given ip including mask if neccessary */
+/* Print a given ip including mask if necessary. */
static void print_ip(const char *prefix, uint32_t ip,
uint32_t mask, int invert)
{
@@ -1040,8 +1078,9 @@ static void print_ip(const char *prefix, uint32_t ip,
printf("/%u.%u.%u.%u", IP_PARTS(mask));
}
-/* We want this to be readable, so only print out neccessary fields.
- * Because that's the kind of world I want to live in. */
+/* We want this to be readable, so only print out necessary fields.
+ * Because that's the kind of world I want to live in.
+ */
void print_rule4(const struct ipt_entry *e,
struct xtc_handle *h, const char *chain, int counters)
{
@@ -1097,8 +1136,10 @@ void print_rule4(const struct ipt_entry *e,
}
printf(" -j %s", target->alias ? target->alias(t) : target_name);
- if (target->save)
+ if (target->save && t->u.user.revision == target->revision)
target->save(&e->ip, t);
+ else if (target->save)
+ printf(unsupported_rev);
else {
/* If the target size is greater than xt_entry_target
* there is something to be saved, we just don't know
@@ -1290,9 +1331,12 @@ int do_command4(int argc, char *argv[], char **table,
unsigned int nsaddrs = 0, ndaddrs = 0;
struct in_addr *saddrs = NULL, *smasks = NULL;
struct in_addr *daddrs = NULL, *dmasks = NULL;
-
+ struct timeval wait_interval = {
+ .tv_sec = 1,
+ };
+ bool wait_interval_set = false;
int verbose = 0;
- bool wait = false;
+ int wait = 0;
const char *chain = NULL;
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
const char *policy = NULL, *newname = NULL;
@@ -1325,10 +1369,9 @@ int do_command4(int argc, char *argv[], char **table,
/* 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:fbvwnt:m:xc:g:46",
+ "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::W::nt:m:xc:g:46",
opts, NULL)) != -1) {
switch (cs.c) {
/*
@@ -1350,8 +1393,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_DELETE, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!') {
+ if (xs_has_arg(argc, argv)) {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_DELETE_NUM;
}
@@ -1361,8 +1403,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_REPLACE, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
else
xtables_error(PARAMETER_PROBLEM,
@@ -1374,8 +1415,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_INSERT, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
else rulenum = 1;
break;
@@ -1384,11 +1424,9 @@ int do_command4(int argc, char *argv[], char **table,
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] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
break;
@@ -1396,11 +1434,9 @@ int do_command4(int argc, char *argv[], char **table,
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] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
break;
@@ -1408,8 +1444,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_FLUSH, CMD_NONE,
cs.invert);
if (optarg) chain = optarg;
- else if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
@@ -1417,25 +1452,16 @@ int do_command4(int argc, char *argv[], char **table,
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] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!') {
+ if (xs_has_arg(argc, argv)) {
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");
+ parse_chain(optarg);
add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
@@ -1445,8 +1471,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
cs.invert);
if (optarg) chain = optarg;
- else if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
@@ -1454,8 +1479,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
newname = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
@@ -1468,8 +1492,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_SET_POLICY, CMD_NONE,
cs.invert);
chain = optarg;
- if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (xs_has_arg(argc, argv))
policy = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
@@ -1577,7 +1600,17 @@ int do_command4(int argc, char *argv[], char **table,
"You cannot use `-w' from "
"iptables-restore");
}
- wait = true;
+ wait = parse_wait_time(argc, argv);
+ break;
+
+ case 'W':
+ if (restore) {
+ xtables_error(PARAMETER_PROBLEM,
+ "You cannot use `-W' from "
+ "iptables-restore");
+ }
+ parse_wait_interval(argc, argv, &wait_interval);
+ wait_interval_set = true;
break;
case 'm':
@@ -1626,8 +1659,7 @@ int do_command4(int argc, char *argv[], char **table,
bcnt = strchr(pcnt + 1, ',');
if (bcnt)
bcnt++;
- if (!bcnt && optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
+ if (!bcnt && xs_has_arg(argc, argv))
bcnt = argv[optind++];
if (!bcnt)
xtables_error(PARAMETER_PROBLEM,
@@ -1680,6 +1712,10 @@ int do_command4(int argc, char *argv[], char **table,
cs.invert = FALSE;
}
+ if (!wait && wait_interval_set)
+ xtables_error(PARAMETER_PROBLEM,
+ "--wait-interval only makes sense with --wait\n");
+
if (strcmp(*table, "nat") == 0 &&
((policy != NULL && strcmp(policy, "DROP") == 0) ||
(cs.jumpto != NULL && strcmp(cs.jumpto, "DROP") == 0)))
@@ -1729,15 +1765,13 @@ int do_command4(int argc, char *argv[], char **table,
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);
-
/* Attempt to acquire the xtables lock */
- if (!restore && (xtables_lock(wait) == XT_LOCK_BUSY)) {
- fprintf(stderr, "Another app is currently holding the xtables lock. "
- "Perhaps you want to use the -w option?\n");
+ if (!restore && xtables_lock(wait, &wait_interval) == XT_LOCK_BUSY) {
+ fprintf(stderr, "Another app is currently holding the xtables lock. ");
+ if (wait == 0)
+ fprintf(stderr, "Perhaps you want to use the -w option?\n");
+ else
+ fprintf(stderr, "Stopped waiting after %ds.\n", wait);
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
@@ -1812,10 +1846,11 @@ int do_command4(int argc, char *argv[], char **table,
}
if (!cs.target) {
- /* it is no chain, and we can't load a plugin.
+ /* It is no chain, and we can't load a plugin.
* We cannot know if the plugin is corrupt, non
- * existant OR if the user just misspelled a
- * chain. */
+ * existent OR if the user just misspelled a
+ * chain.
+ */
#ifdef IPT_F_GOTO
if (cs.fw.ip.flags & IPT_F_GOTO)
xtables_error(PARAMETER_PROBLEM,
diff --git a/iptables/iptables.xslt b/iptables/iptables.xslt
index d6a432cf..afe6d0d0 100644
--- a/iptables/iptables.xslt
+++ b/iptables/iptables.xslt
@@ -13,7 +13,7 @@
<!-- output conditions of a rule but not an action -->
<xsl:template match="iptables-rules/table/chain/rule/conditions/*">
- <!-- <match> is the psuedo module when a match module doesn't need to be loaded
+ <!-- <match> is the pseudo module when a match module doesn't need to be loaded
and when -m does not need to be inserted -->
<xsl:if test="name() != 'match'">
<xsl:text> -m </xsl:text><xsl:value-of select="name()"/>
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
new file mode 100644
index 00000000..0e13b8c5
--- /dev/null
+++ b/iptables/nft-arp.c
@@ -0,0 +1,672 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ * 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 <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if_arp.h>
+
+#include <xtables.h>
+#include <libiptc/libxtc.h>
+#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+
+#include <linux/netfilter_arp/arp_tables.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "nft-shared.h"
+#include "nft-arp.h"
+#include "nft.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 int nft_arp_add(struct nftnl_rule *r, void *data)
+{
+ struct arptables_command_state *cs = data;
+ struct arpt_entry *fw = &cs->fw;
+ uint32_t op;
+ int ret = 0;
+
+ if (fw->arp.iniface[0] != '\0') {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_IN);
+ add_iniface(r, fw->arp.iniface, op);
+ }
+
+ if (fw->arp.outiface[0] != '\0') {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_OUT);
+ add_outiface(r, fw->arp.outiface, op);
+ }
+
+ if (fw->arp.arhrd != 0) {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHRD);
+ add_payload(r, offsetof(struct arphdr, ar_hrd), 2,
+ NFT_PAYLOAD_NETWORK_HEADER);
+ add_cmp_u16(r, fw->arp.arhrd, op);
+ }
+
+ if (fw->arp.arpro != 0) {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPPRO);
+ add_payload(r, offsetof(struct arphdr, ar_pro), 2,
+ NFT_PAYLOAD_NETWORK_HEADER);
+ add_cmp_u16(r, fw->arp.arpro, op);
+ }
+
+ if (fw->arp.arhln != 0) {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHLN);
+ add_proto(r, offsetof(struct arphdr, ar_hln), 1,
+ fw->arp.arhln, op);
+ }
+
+ add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, NFT_CMP_EQ);
+
+ if (fw->arp.arpop != 0) {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPOP);
+ add_payload(r, offsetof(struct arphdr, ar_op), 2,
+ NFT_PAYLOAD_NETWORK_HEADER);
+ add_cmp_u16(r, fw->arp.arpop, op);
+ }
+
+ if (fw->arp.src_devaddr.addr[0] != '\0') {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCDEVADDR);
+ add_payload(r, sizeof(struct arphdr), fw->arp.arhln,
+ NFT_PAYLOAD_NETWORK_HEADER);
+ add_cmp_ptr(r, op, fw->arp.src_devaddr.addr, fw->arp.arhln);
+ }
+
+ if (fw->arp.src.s_addr != 0) {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCIP);
+ add_addr(r, sizeof(struct arphdr) + fw->arp.arhln,
+ &fw->arp.src.s_addr, &fw->arp.smsk.s_addr,
+ sizeof(struct in_addr), op);
+ }
+
+ if (fw->arp.tgt_devaddr.addr[0] != '\0') {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTDEVADDR);
+ add_payload(r, sizeof(struct arphdr) + fw->arp.arhln + 4,
+ fw->arp.arhln, NFT_PAYLOAD_NETWORK_HEADER);
+ add_cmp_ptr(r, op, fw->arp.tgt_devaddr.addr, fw->arp.arhln);
+ }
+
+ if (fw->arp.tgt.s_addr != 0) {
+ op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTIP);
+ add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr),
+ &fw->arp.tgt.s_addr, &fw->arp.tmsk.s_addr,
+ sizeof(struct in_addr), op);
+ }
+
+ /* 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;
+
+ 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) {
+ /* No goto in arptables */
+ ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
+ }
+
+ return ret;
+}
+
+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;
+
+ return result;
+}
+
+static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ void *data)
+{
+ struct arptables_command_state *cs = data;
+ struct arpt_entry *fw = &cs->fw;
+ uint8_t flags = 0;
+
+ parse_meta(e, ctx->meta.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 *target, void *data)
+{
+ struct arptables_command_state *cs = data;
+
+ cs->target = target;
+}
+
+static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
+ void *data)
+{
+ struct arptables_command_state *cs = data;
+
+ cs->jumpto = jumpto;
+}
+
+static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
+{
+ mask->s_addr = ctx->bitwise.mask[0];
+}
+
+static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e, void *data)
+{
+ struct arptables_command_state *cs = data;
+ struct arpt_entry *fw = &cs->fw;
+ struct in_addr addr;
+ unsigned short int ar_hrd, ar_pro, ar_op, ar_hln;
+ bool inv;
+
+ switch (ctx->payload.offset) {
+ case offsetof(struct arphdr, ar_hrd):
+ get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv);
+ 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(e, &ar_pro, sizeof(ar_pro), &inv);
+ 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(e, &ar_op, sizeof(ar_op), &inv);
+ fw->arp.arpop = ar_op;
+ fw->arp.arpop_mask = 0xffff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPOP;
+ break;
+ case offsetof(struct arphdr, ar_hln):
+ get_cmp_data(e, &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 < 0)
+ break;
+
+ if (ctx->payload.offset == sizeof(struct arphdr) +
+ fw->arp.arhln) {
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ fw->arp.src.s_addr = addr.s_addr;
+ if (ctx->flags & NFT_XT_CTX_BITWISE) {
+ parse_mask_ipv4(ctx, &fw->arp.smsk);
+ ctx->flags &= ~NFT_XT_CTX_BITWISE;
+ } else {
+ fw->arp.smsk.s_addr = 0xffffffff;
+ }
+
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_SRCIP;
+ } else if (ctx->payload.offset == sizeof(struct arphdr) +
+ fw->arp.arhln +
+ sizeof(struct in_addr)) {
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ fw->arp.tgt.s_addr = addr.s_addr;
+ if (ctx->flags & NFT_XT_CTX_BITWISE) {
+ parse_mask_ipv4(ctx, &fw->arp.tmsk);
+ ctx->flags &= ~NFT_XT_CTX_BITWISE;
+ } else {
+ fw->arp.tmsk.s_addr = 0xffffffff;
+ }
+
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_TGTIP;
+ }
+ break;
+ }
+}
+
+void nft_rule_to_arptables_command_state(struct nftnl_rule *r,
+ struct arptables_command_state *cs)
+{
+ struct nftnl_expr_iter *iter;
+ struct nftnl_expr *expr;
+ int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
+ struct nft_xt_ctx ctx = {
+ .state.cs_arp = cs,
+ .family = family,
+ };
+
+ iter = nftnl_expr_iter_create(r);
+ if (iter == NULL)
+ return;
+
+ ctx.iter = iter;
+ expr = nftnl_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+
+ if (strcmp(name, "counter") == 0)
+ nft_parse_counter(expr, &ctx.state.cs_arp->fw.counters);
+ else if (strcmp(name, "payload") == 0)
+ nft_parse_payload(&ctx, expr);
+ else if (strcmp(name, "meta") == 0)
+ nft_parse_meta(&ctx, expr);
+ else if (strcmp(name, "bitwise") == 0)
+ nft_parse_bitwise(&ctx, expr);
+ else if (strcmp(name, "cmp") == 0)
+ nft_parse_cmp(&ctx, expr);
+ else if (strcmp(name, "immediate") == 0)
+ nft_parse_immediate(&ctx, expr);
+ else if (strcmp(name, "target") == 0)
+ nft_parse_target(&ctx, expr);
+
+ expr = nftnl_expr_iter_next(iter);
+ }
+
+ nftnl_expr_iter_destroy(iter);
+
+ if (cs->jumpto != NULL)
+ return;
+
+ if (cs->target != NULL && cs->target->name != NULL)
+ cs->target = xtables_find_target(cs->target->name, XTF_TRY_LOAD);
+ else
+ cs->jumpto = "";
+}
+
+static void nft_arp_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 (pol) {
+ printf(" (policy %s", pol);
+ if (!(format & FMT_NOCOUNTS)) {
+ fputc(' ', stdout);
+ xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
+ fputs("packets, ", stdout);
+ xtables_print_num(counters->bcnt, (format|FMT_NOTABLE));
+ fputs("bytes", stdout);
+ }
+ printf(")\n");
+ } else {
+ printf(" (%u references)\n", refs);
+ }
+}
+
+static void print_fw_details(struct arpt_entry *fw, unsigned int format)
+{
+ char buf[BUFSIZ];
+ char iface[IFNAMSIZ+2];
+ int print_iface = 0;
+ int i;
+
+ 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)));
+ strncat(buf, mask_to_dotted(&(fw->arp.smsk)),
+ sizeof(buf) - strlen(buf) - 1);
+ 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)));
+ strncat(buf, mask_to_dotted(&(fw->arp.tmsk)),
+ sizeof(buf) - strlen(buf) - 1);
+ printf("-d %s ", buf);
+ }
+
+ for (i = 0; i <ARPT_DEV_ADDR_LEN_MAX; i++)
+ if (fw->arp.tgt_devaddr.mask[i] != 0)
+ break;
+ if (i == ARPT_DEV_ADDR_LEN_MAX)
+ goto after_devdst;
+ printf("%s", fw->arp.invflags & ARPT_INV_TGTDEVADDR
+ ? "! " : "");
+ printf("--dst-mac ");
+ print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
+ (unsigned char *)fw->arp.tgt_devaddr.mask, ETH_ALEN);
+ printf(" ");
+
+after_devdst:
+
+ if (fw->arp.arhln_mask != 0) {
+ printf("%s", fw->arp.invflags & ARPT_INV_ARPHLN
+ ? "! " : "");
+ printf("--h-length %d", fw->arp.arhln);
+ if (fw->arp.arhln_mask != 255)
+ printf("/%d", fw->arp.arhln_mask);
+ printf(" ");
+ }
+
+ if (fw->arp.arpop_mask != 0) {
+ int tmp = ntohs(fw->arp.arpop);
+
+ printf("%s", fw->arp.invflags & ARPT_INV_ARPOP
+ ? "! " : "");
+ if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
+ printf("--opcode %s", opcodes[tmp-1]);
+ else
+
+ if (fw->arp.arpop_mask != 65535)
+ printf("/%d", ntohs(fw->arp.arpop_mask));
+ printf(" ");
+ }
+
+ if (fw->arp.arhrd_mask != 0) {
+ uint16_t tmp = ntohs(fw->arp.arhrd);
+
+ printf("%s", fw->arp.invflags & ARPT_INV_ARPHRD
+ ? "! " : "");
+ if (tmp == 1 && !(format & FMT_NUMERIC))
+ printf("--h-type %s", "Ethernet");
+ else
+ printf("--h-type %u", tmp);
+ if (fw->arp.arhrd_mask != 65535)
+ printf("/%d", ntohs(fw->arp.arhrd_mask));
+ printf(" ");
+ }
+
+ if (fw->arp.arpro_mask != 0) {
+ int tmp = ntohs(fw->arp.arpro);
+
+ printf("%s", fw->arp.invflags & ARPT_INV_ARPPRO
+ ? "! " : "");
+ if (tmp == 0x0800 && !(format & FMT_NUMERIC))
+ printf("--proto-type %s", "IPv4");
+ else
+ printf("--proto-type 0x%x", tmp);
+ if (fw->arp.arpro_mask != 65535)
+ printf("/%x", ntohs(fw->arp.arpro_mask));
+ printf(" ");
+ }
+}
+
+static void
+nft_arp_print_firewall(struct nftnl_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct arptables_command_state cs = {};
+
+ nft_rule_to_arptables_command_state(r, &cs);
+
+ if (format & FMT_LINENUMBERS)
+ printf("%u ", num);
+
+ print_fw_details(&cs.fw, format);
+
+ if (cs.jumpto != NULL && strcmp(cs.jumpto, "") != 0) {
+ printf("-j %s", cs.jumpto);
+ } else if (cs.target) {
+ printf("-j %s", cs.target->name);
+ cs.target->print(&cs.fw, cs.target->t, format & FMT_NUMERIC);
+ }
+
+ if (!(format & FMT_NOCOUNTS)) {
+ printf(", pcnt=");
+ xtables_print_num(cs.fw.counters.pcnt, format);
+ printf("-- bcnt=");
+ xtables_print_num(cs.fw.counters.bcnt, format);
+ }
+
+ if (!(format & FMT_NONEWLINE))
+ 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.iniface,
+ a->arp.outiface,
+ (unsigned char *)a->arp.iniface_mask,
+ (unsigned char *)a->arp.outiface_mask,
+ b->arp.iniface,
+ b->arp.outiface,
+ (unsigned char *)b->arp.iniface_mask,
+ (unsigned char *)b->arp.outiface_mask);
+}
+
+static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+ void *data)
+{
+ const struct arptables_command_state *cs = data;
+ struct arptables_command_state this = {};
+
+ /* Delete by matching rule case */
+ nft_rule_to_arptables_command_state(r, &this);
+
+ if (!nft_arp_is_same(cs, &this))
+ return false;
+
+ if (!compare_targets(cs->target, this.target))
+ return false;
+
+ if (strcmp(cs->jumpto, this.jumpto) != 0)
+ return false;
+
+ return true;
+}
+
+struct nft_family_ops nft_family_ops_arp = {
+ .add = nft_arp_add,
+ .is_same = nft_arp_is_same,
+ .print_payload = NULL,
+ .parse_meta = nft_arp_parse_meta,
+ .parse_payload = nft_arp_parse_payload,
+ .parse_immediate = nft_arp_parse_immediate,
+ .print_header = nft_arp_print_header,
+ .print_firewall = nft_arp_print_firewall,
+ .save_firewall = NULL,
+ .save_counters = NULL,
+ .post_parse = NULL,
+ .rule_find = nft_arp_rule_find,
+ .parse_target = nft_arp_parse_target,
+};
diff --git a/iptables/nft-arp.h b/iptables/nft-arp.h
new file mode 100644
index 00000000..05889b49
--- /dev/null
+++ b/iptables/nft-arp.h
@@ -0,0 +1,16 @@
+#ifndef _NFT_ARP_H_
+#define _NFT_ARP_H_
+
+extern char *opcodes[];
+#define NUMOPCODES 9
+
+struct arptables_command_state {
+ struct arpt_entry fw;
+ struct xtables_target *target;
+ const char *jumpto;
+};
+
+void nft_rule_to_arptables_command_state(struct nftnl_rule *r,
+ struct arptables_command_state *cs);
+
+#endif
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
new file mode 100644
index 00000000..22940cf7
--- /dev/null
+++ b/iptables/nft-bridge.c
@@ -0,0 +1,647 @@
+/*
+ * (C) 2014 by Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <inttypes.h>
+
+#include <xtables.h>
+#include <libiptc/libxtc.h>
+#include <linux/netfilter/nf_tables.h>
+#include <ebtables/ethernetdb.h>
+
+#include "nft-shared.h"
+#include "nft-bridge.h"
+#include "nft.h"
+
+void ebt_cs_clean(struct ebtables_command_state *cs)
+{
+ struct ebt_match *m, *nm;
+
+ xtables_rule_matches_free(&cs->matches);
+
+ for (m = cs->match_list; m;) {
+ nm = m->next;
+ if (!m->ismatch)
+ free(m->u.watcher->t);
+ free(m);
+ m = nm;
+ }
+}
+
+/* 0: default, print only 2 digits if necessary
+ * 2: always print 2 digits, a printed mac address
+ * then always has the same length
+ */
+int ebt_printstyle_mac;
+
+static void ebt_print_mac(const unsigned char *mac)
+{
+ if (ebt_printstyle_mac == 2) {
+ int j;
+ for (j = 0; j < ETH_ALEN; j++)
+ printf("%02x%s", mac[j],
+ (j==ETH_ALEN-1) ? "" : ":");
+ } else
+ printf("%s", ether_ntoa((struct ether_addr *) mac));
+}
+
+/* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */
+static void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
+{
+ char hlpmsk[6] = {};
+
+ if (!memcmp(mac, eb_mac_type_unicast, 6) &&
+ !memcmp(mask, eb_msk_type_unicast, 6))
+ printf("Unicast");
+ else if (!memcmp(mac, eb_mac_type_multicast, 6) &&
+ !memcmp(mask, eb_msk_type_multicast, 6))
+ printf("Multicast");
+ else if (!memcmp(mac, eb_mac_type_broadcast, 6) &&
+ !memcmp(mask, eb_msk_type_broadcast, 6))
+ printf("Broadcast");
+ else if (!memcmp(mac, eb_mac_type_bridge_group, 6) &&
+ !memcmp(mask, eb_msk_type_bridge_group, 6))
+ printf("BGA");
+ else {
+ ebt_print_mac(mac);
+ if (memcmp(mask, hlpmsk, 6)) {
+ printf("/");
+ ebt_print_mac(mask);
+ }
+ }
+}
+
+static uint16_t ipt_to_ebt_flags(uint8_t invflags)
+{
+ uint16_t result = 0;
+
+ if (invflags & IPT_INV_VIA_IN)
+ result |= EBT_IIN;
+
+ if (invflags & IPT_INV_VIA_OUT)
+ result |= EBT_IOUT;
+
+ if (invflags & IPT_INV_PROTO)
+ result |= EBT_IPROTO;
+
+ return result;
+}
+
+static void add_logical_iniface(struct nftnl_rule *r, char *iface, uint32_t op)
+{
+ int iface_len;
+
+ iface_len = strlen(iface);
+
+ add_meta(r, NFT_META_BRI_IIFNAME);
+ if (iface[iface_len - 1] == '+')
+ add_cmp_ptr(r, op, iface, iface_len - 1);
+ else
+ add_cmp_ptr(r, op, iface, iface_len + 1);
+}
+
+static void add_logical_outiface(struct nftnl_rule *r, char *iface, uint32_t op)
+{
+ int iface_len;
+
+ iface_len = strlen(iface);
+
+ add_meta(r, NFT_META_BRI_OIFNAME);
+ if (iface[iface_len - 1] == '+')
+ add_cmp_ptr(r, op, iface, iface_len - 1);
+ else
+ add_cmp_ptr(r, op, iface, iface_len + 1);
+}
+
+/* TODO: Use generic add_action() once we convert this to use
+ * iptables_command_state.
+ */
+static int _add_action(struct nftnl_rule *r, struct ebtables_command_state *cs)
+{
+ int ret = 0;
+
+ if (cs->jumpto == NULL || strcmp(cs->jumpto, "CONTINUE") == 0)
+ return 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 jump to chain */
+ ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
+ }
+
+ return ret;
+}
+
+static int nft_bridge_add(struct nftnl_rule *r, void *data)
+{
+ struct ebtables_command_state *cs = data;
+ struct ebt_match *iter;
+ struct ebt_entry *fw = &cs->fw;
+ uint32_t op;
+ char *addr;
+
+ if (fw->in[0] != '\0') {
+ op = nft_invflags2cmp(fw->invflags, EBT_IIN);
+ add_iniface(r, fw->in, op);
+ }
+
+ if (fw->out[0] != '\0') {
+ op = nft_invflags2cmp(fw->invflags, EBT_IOUT);
+ add_outiface(r, fw->out, op);
+ }
+
+ if (fw->logical_in[0] != '\0') {
+ op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALIN);
+ add_logical_iniface(r, fw->logical_in, op);
+ }
+
+ if (fw->logical_out[0] != '\0') {
+ op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALOUT);
+ add_logical_outiface(r, fw->logical_out, op);
+ }
+
+ addr = ether_ntoa((struct ether_addr *) fw->sourcemac);
+ if (strcmp(addr, "0:0:0:0:0:0") != 0) {
+ op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE);
+ add_payload(r, offsetof(struct ethhdr, h_source), 6,
+ NFT_PAYLOAD_LL_HEADER);
+ add_cmp_ptr(r, op, fw->sourcemac, 6);
+ }
+
+ addr = ether_ntoa((struct ether_addr *) fw->destmac);
+ if (strcmp(addr, "0:0:0:0:0:0") != 0) {
+ op = nft_invflags2cmp(fw->invflags, EBT_IDEST);
+ add_payload(r, offsetof(struct ethhdr, h_dest), 6,
+ NFT_PAYLOAD_LL_HEADER);
+ add_cmp_ptr(r, op, fw->destmac, 6);
+ }
+
+ if (fw->ethproto != 0) {
+ op = nft_invflags2cmp(fw->invflags, EBT_IPROTO);
+ add_payload(r, offsetof(struct ethhdr, h_proto), 2,
+ NFT_PAYLOAD_LL_HEADER);
+ add_cmp_u16(r, fw->ethproto, op);
+ }
+
+ add_compat(r, fw->ethproto, fw->invflags);
+
+ for (iter = cs->match_list; iter; iter = iter->next) {
+ if (iter->ismatch) {
+ if (add_match(r, iter->u.match->m))
+ break;
+ } else {
+ if (add_target(r, iter->u.watcher->t))
+ break;
+ }
+ }
+
+ if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
+ return -1;
+
+ return _add_action(r, cs);
+}
+
+static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e, void *data)
+{
+ struct ebtables_command_state *cs = data;
+ struct ebt_entry *fw = &cs->fw;
+ uint8_t flags = 0;
+ int iface = 0;
+ const void *ifname;
+ uint32_t len;
+
+ iface = parse_meta(e, ctx->meta.key, fw->in, fw->in_mask,
+ fw->out, fw->out_mask, &flags);
+ if (!iface)
+ goto out;
+
+ switch (ctx->meta.key) {
+ case NFT_META_BRI_IIFNAME:
+ ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ flags |= IPT_INV_VIA_IN;
+
+ memcpy(fw->logical_in, ifname, len);
+
+ if (fw->logical_in[len] == '\0')
+ memset(fw->in_mask, 0xff, len);
+ else {
+ fw->logical_in[len] = '+';
+ fw->logical_in[len+1] = '\0';
+ memset(fw->in_mask, 0xff, len + 1);
+ }
+ break;
+ case NFT_META_BRI_OIFNAME:
+ ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ flags |= IPT_INV_VIA_OUT;
+
+ memcpy(fw->logical_out, ifname, len);
+
+ if (fw->logical_out[len] == '\0')
+ memset(fw->out_mask, 0xff, len);
+ else {
+ fw->logical_out[len] = '+';
+ fw->logical_out[len+1] = '\0';
+ memset(fw->out_mask, 0xff, len + 1);
+ }
+ break;
+ default:
+ break;
+ }
+
+out:
+ fw->invflags |= ipt_to_ebt_flags(flags);
+}
+
+static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e, void *data)
+{
+ struct ebtables_command_state *cs = data;
+ struct ebt_entry *fw = &cs->fw;
+ unsigned char addr[ETH_ALEN];
+ unsigned short int ethproto;
+ bool inv;
+ int i;
+
+ switch (ctx->payload.offset) {
+ case offsetof(struct ethhdr, h_dest):
+ get_cmp_data(e, addr, sizeof(addr), &inv);
+ for (i = 0; i < ETH_ALEN; i++)
+ fw->destmac[i] = addr[i];
+ if (inv)
+ fw->invflags |= EBT_IDEST;
+ break;
+ case offsetof(struct ethhdr, h_source):
+ get_cmp_data(e, addr, sizeof(addr), &inv);
+ for (i = 0; i < ETH_ALEN; i++)
+ fw->sourcemac[i] = addr[i];
+ if (inv)
+ fw->invflags |= EBT_ISOURCE;
+ break;
+ case offsetof(struct ethhdr, h_proto):
+ get_cmp_data(e, &ethproto, sizeof(ethproto), &inv);
+ fw->ethproto = ethproto;
+ if (inv)
+ fw->invflags |= EBT_IPROTO;
+ break;
+ }
+}
+
+static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto,
+ void *data)
+{
+ struct ebtables_command_state *cs = data;
+
+ cs->jumpto = jumpto;
+}
+
+static void parse_watcher(void *object, struct ebt_match **match_list,
+ bool ismatch)
+{
+ struct ebt_match *m;
+
+ m = calloc(1, sizeof(struct ebt_match));
+ if (m == NULL)
+ xtables_error(OTHER_PROBLEM, "Can't allocate memory");
+
+ if (ismatch)
+ m->u.match = object;
+ else
+ m->u.watcher = object;
+
+ m->ismatch = ismatch;
+ if (*match_list == NULL)
+ *match_list = m;
+ else
+ (*match_list)->next = m;
+}
+
+static void nft_bridge_parse_match(struct xtables_match *m, void *data)
+{
+ struct ebtables_command_state *cs = data;
+
+ parse_watcher(m, &cs->match_list, true);
+}
+
+static void nft_bridge_parse_target(struct xtables_target *t, void *data)
+{
+ struct ebtables_command_state *cs = data;
+
+ /* harcoded names :-( */
+ if (strcmp(t->name, "log") == 0 ||
+ strcmp(t->name, "nflog") == 0) {
+ parse_watcher(t, &cs->match_list, false);
+ return;
+ }
+
+ cs->target = t;
+}
+
+void nft_rule_to_ebtables_command_state(struct nftnl_rule *r,
+ struct ebtables_command_state *cs)
+{
+ struct nftnl_expr_iter *iter;
+ struct nftnl_expr *expr;
+ int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
+ struct nft_xt_ctx ctx = {
+ .state.cs_eb = cs,
+ .family = family,
+ };
+
+ iter = nftnl_expr_iter_create(r);
+ if (iter == NULL)
+ return;
+
+ expr = nftnl_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+
+ if (strcmp(name, "counter") == 0)
+ nft_parse_counter(expr, &cs->counters);
+ else if (strcmp(name, "payload") == 0)
+ nft_parse_payload(&ctx, expr);
+ else if (strcmp(name, "meta") == 0)
+ nft_parse_meta(&ctx, expr);
+ else if (strcmp(name, "bitwise") == 0)
+ nft_parse_bitwise(&ctx, expr);
+ else if (strcmp(name, "cmp") == 0)
+ nft_parse_cmp(&ctx, expr);
+ else if (strcmp(name, "immediate") == 0)
+ nft_parse_immediate(&ctx, expr);
+ else if (strcmp(name, "match") == 0)
+ nft_parse_match(&ctx, expr);
+ else if (strcmp(name, "target") == 0)
+ nft_parse_target(&ctx, expr);
+
+ expr = nftnl_expr_iter_next(iter);
+ }
+
+ nftnl_expr_iter_destroy(iter);
+
+ if (cs->jumpto != NULL)
+ return;
+
+ if (cs->target != NULL && cs->target->name != NULL)
+ cs->target = xtables_find_target(cs->target->name, XTF_TRY_LOAD);
+ else
+ cs->jumpto = "CONTINUE";
+}
+
+static void print_iface(const char *iface)
+{
+ char *c;
+
+ if ((c = strchr(iface, IF_WILDCARD)))
+ *c = '+';
+ printf("%s ", iface);
+ if (c)
+ *c = IF_WILDCARD;
+}
+
+static void nft_bridge_print_table_header(const char *tablename)
+{
+ printf("Bridge table: %s\n\n", tablename);
+}
+
+static void nft_bridge_print_header(unsigned int format, const char *chain,
+ const char *pol,
+ const struct xt_counters *counters,
+ bool basechain, uint32_t refs)
+{
+ printf("Bridge chain: %s, entries: %u, policy: %s\n",
+ chain, refs, basechain ? pol : "RETURN");
+}
+
+static void nft_bridge_print_firewall(struct nftnl_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct xtables_match *matchp;
+ struct xtables_target *watcherp;
+ struct ebt_match *m;
+ struct ebtables_command_state cs = {};
+ char *addr;
+
+ nft_rule_to_ebtables_command_state(r, &cs);
+
+ if (format & FMT_LINENUMBERS)
+ printf("%d ", num);
+
+ /* Dont print anything about the protocol if no protocol was
+ * specified, obviously this means any protocol will do. */
+ if (cs.fw.ethproto != 0) {
+ printf("-p ");
+ if (cs.fw.invflags & EBT_IPROTO)
+ printf("! ");
+ if (cs.fw.bitmask & EBT_802_3)
+ printf("Length ");
+ else {
+ struct ethertypeent *ent;
+
+ ent = getethertypebynumber(ntohs(cs.fw.ethproto));
+ if (!ent)
+ printf("0x%x ", ntohs(cs.fw.ethproto));
+ else
+ printf("%s ", ent->e_name);
+ }
+ }
+
+ addr = ether_ntoa((struct ether_addr *) cs.fw.sourcemac);
+ if (strcmp(addr, "0:0:0:0:0:0") != 0) {
+ printf("-s ");
+ if (cs.fw.invflags & EBT_ISOURCE)
+ printf("! ");
+ ebt_print_mac_and_mask(cs.fw.sourcemac, cs.fw.sourcemsk);
+ printf(" ");
+ }
+
+ addr = ether_ntoa((struct ether_addr *) cs.fw.destmac);
+ if (strcmp(addr, "0:0:0:0:0:0") != 0) {
+ printf("-d ");
+ if (cs.fw.invflags & EBT_IDEST)
+ printf("! ");
+ ebt_print_mac_and_mask(cs.fw.destmac, cs.fw.destmsk);
+ printf(" ");
+ }
+
+ if (cs.fw.in[0] != '\0') {
+ printf("-i ");
+ if (cs.fw.invflags & EBT_IIN)
+ printf("! ");
+ print_iface(cs.fw.in);
+ }
+
+ if (cs.fw.logical_in[0] != '\0') {
+ printf("--logical-in ");
+ if (cs.fw.invflags & EBT_ILOGICALIN)
+ printf("! ");
+ print_iface(cs.fw.logical_in);
+ }
+
+ if (cs.fw.logical_out[0] != '\0') {
+ printf("--logical-out ");
+ if (cs.fw.invflags & EBT_ILOGICALOUT)
+ printf("! ");
+ print_iface(cs.fw.logical_out);
+ }
+
+ if (cs.fw.out[0] != '\0') {
+ printf("-o ");
+ if (cs.fw.invflags & EBT_IOUT)
+ printf("! ");
+ print_iface(cs.fw.out);
+ }
+
+ for (m = cs.match_list; m; m = m->next) {
+ if (m->ismatch) {
+ matchp = m->u.match;
+ if (matchp->print != NULL) {
+ matchp->print(&cs.fw, matchp->m,
+ format & FMT_NUMERIC);
+ }
+ } else {
+ watcherp = m->u.watcher;
+ if (watcherp->print != NULL) {
+ watcherp->print(&cs.fw, watcherp->t,
+ format & FMT_NUMERIC);
+ }
+ }
+ }
+
+ printf("-j ");
+
+ if (cs.jumpto != NULL)
+ printf("%s", cs.jumpto);
+ else if (cs.target != NULL && cs.target->print != NULL)
+ cs.target->print(&cs.fw, cs.target->t, format & FMT_NUMERIC);
+
+ if (!(format & FMT_NOCOUNTS))
+ printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"",
+ (uint64_t)cs.counters.pcnt, (uint64_t)cs.counters.bcnt);
+
+ if (!(format & FMT_NONEWLINE))
+ fputc('\n', stdout);
+
+ ebt_cs_clean(&cs);
+}
+
+static bool nft_bridge_is_same(const void *data_a, const void *data_b)
+{
+ const struct ebt_entry *a = data_a;
+ const struct ebt_entry *b = data_b;
+ int i;
+
+ if (a->ethproto != b->ethproto ||
+ /* FIXME: a->flags != b->flags || */
+ a->invflags != b->invflags) {
+ DEBUGP("different proto/flags/invflags\n");
+ return false;
+ }
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ if (a->sourcemac[i] != b->sourcemac[i]) {
+ DEBUGP("different source mac %x, %x (%d)\n",
+ a->sourcemac[i] & 0xff, b->sourcemac[i] & 0xff, i);
+ return false;
+ }
+
+ if (a->destmac[i] != b->destmac[i]) {
+ DEBUGP("different destination mac %x, %x (%d)\n",
+ a->destmac[i] & 0xff, b->destmac[i] & 0xff, i);
+ return false;
+ }
+ }
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a->logical_in[i] != b->logical_in[i]) {
+ DEBUGP("different logical iniface %x, %x (%d)\n",
+ a->logical_in[i] & 0xff, b->logical_in[i] & 0xff, i);
+ return false;
+ }
+
+ if (a->logical_out[i] != b->logical_out[i]) {
+ DEBUGP("different logical outiface %x, %x (%d)\n",
+ a->logical_out[i] & 0xff, b->logical_out[i] & 0xff, i);
+ return false;
+ }
+ }
+
+ return is_same_interfaces((char *)a->in,
+ (char *)a->out,
+ a->in_mask,
+ a->out_mask,
+ (char *)b->in,
+ (char *)b->out,
+ b->in_mask,
+ b->out_mask);
+}
+
+static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+ void *data)
+{
+ struct ebtables_command_state *cs = data;
+ struct ebtables_command_state this = {};
+
+ nft_rule_to_ebtables_command_state(r, &this);
+
+ DEBUGP("comparing with... ");
+
+ if (!nft_bridge_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 (cs->jumpto != NULL && strcmp(cs->jumpto, this.jumpto) != 0) {
+ DEBUGP("Different verdict\n");
+ return false;
+ }
+
+ return true;
+}
+
+struct nft_family_ops nft_family_ops_bridge = {
+ .add = nft_bridge_add,
+ .is_same = nft_bridge_is_same,
+ .print_payload = NULL,
+ .parse_meta = nft_bridge_parse_meta,
+ .parse_payload = nft_bridge_parse_payload,
+ .parse_immediate = nft_bridge_parse_immediate,
+ .parse_match = nft_bridge_parse_match,
+ .parse_target = nft_bridge_parse_target,
+ .print_table_header = nft_bridge_print_table_header,
+ .print_header = nft_bridge_print_header,
+ .print_firewall = nft_bridge_print_firewall,
+ .save_firewall = NULL,
+ .save_counters = NULL,
+ .post_parse = NULL,
+ .rule_find = nft_bridge_rule_find,
+};
diff --git a/iptables/nft-bridge.h b/iptables/nft-bridge.h
new file mode 100644
index 00000000..1c37a5f6
--- /dev/null
+++ b/iptables/nft-bridge.h
@@ -0,0 +1,171 @@
+#ifndef _NFT_BRIDGE_H_
+#define _NFT_BRIDGE_H_
+
+#include <netinet/in.h>
+//#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/ethernet.h>
+#include <libiptc/libxtc.h>
+
+/* We use replace->flags, so we can't use the following values:
+ * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO */
+#define LIST_N 0x04
+#define LIST_C 0x08
+#define LIST_X 0x10
+#define LIST_MAC2 0x20
+
+/* Be backwards compatible, so don't use '+' in kernel */
+#define IF_WILDCARD 1
+
+extern unsigned char eb_mac_type_unicast[ETH_ALEN];
+extern unsigned char eb_msk_type_unicast[ETH_ALEN];
+extern unsigned char eb_mac_type_multicast[ETH_ALEN];
+extern unsigned char eb_msk_type_multicast[ETH_ALEN];
+extern unsigned char eb_mac_type_broadcast[ETH_ALEN];
+extern unsigned char eb_msk_type_broadcast[ETH_ALEN];
+extern unsigned char eb_mac_type_bridge_group[ETH_ALEN];
+extern unsigned char eb_msk_type_bridge_group[ETH_ALEN];
+
+int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mask);
+
+/* From: include/linux/netfilter_bridge/ebtables.h
+ *
+ * Adapted for the need of the ebtables-compat.
+ */
+
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
+/* verdicts >0 are "branches" */
+#define EBT_ACCEPT -1
+#define EBT_DROP -2
+#define EBT_CONTINUE -3
+#define EBT_RETURN -4
+#define NUM_STANDARD_TARGETS 4
+
+#define EBT_ENTRY_OR_ENTRIES 0x01
+/* these are the normal masks */
+#define EBT_NOPROTO 0x02
+#define EBT_802_3 0x04
+#define EBT_SOURCEMAC 0x08
+#define EBT_DESTMAC 0x10
+#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
+ | EBT_ENTRY_OR_ENTRIES)
+
+#define EBT_IPROTO 0x01
+#define EBT_IIN 0x02
+#define EBT_IOUT 0x04
+#define EBT_ISOURCE 0x8
+#define EBT_IDEST 0x10
+#define EBT_ILOGICALIN 0x20
+#define EBT_ILOGICALOUT 0x40
+#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+
+/* ebtables target modules store the verdict inside an int. We can
+ * reclaim a part of this int for backwards compatible extensions.
+ * The 4 lsb are more than enough to store the verdict.
+ */
+#define EBT_VERDICT_BITS 0x0000000F
+
+/* Fake ebt_entry */
+struct ebt_entry {
+ /* this needs to be the first field */
+ unsigned int bitmask;
+ unsigned int invflags;
+ uint16_t ethproto;
+ /* the physical in-dev */
+ char in[IFNAMSIZ];
+ /* the logical in-dev */
+ char logical_in[IFNAMSIZ];
+ /* the physical out-dev */
+ char out[IFNAMSIZ];
+ /* the logical out-dev */
+ char logical_out[IFNAMSIZ];
+ unsigned char sourcemac[ETH_ALEN];
+ unsigned char sourcemsk[ETH_ALEN];
+ unsigned char destmac[ETH_ALEN];
+ unsigned char destmsk[ETH_ALEN];
+
+ unsigned char in_mask[IFNAMSIZ];
+ unsigned char out_mask[IFNAMSIZ];
+};
+
+/* trick for ebtables-compat, since watchers are targets */
+struct ebt_match {
+ struct ebt_match *next;
+ union {
+ struct xtables_match *match;
+ struct xtables_target *watcher;
+ } u;
+ bool ismatch;
+};
+
+struct ebtables_command_state {
+ struct ebt_entry fw;
+ struct xtables_target *target;
+ struct xtables_rule_match *matches;
+ struct ebt_match *match_list;
+ const char *jumpto;
+ struct xt_counters counters;
+ int invert;
+ int c;
+ char **argv;
+ int proto_used;
+ char *protocol;
+ unsigned int options;
+};
+
+void nft_rule_to_ebtables_command_state(struct nftnl_rule *r,
+ struct ebtables_command_state *cs);
+
+static const char *ebt_standard_targets[NUM_STANDARD_TARGETS] = {
+ "ACCEPT",
+ "DROP",
+ "CONTINUE",
+ "RETURN",
+};
+
+static inline const char *nft_ebt_standard_target(unsigned int num)
+{
+ if (num > NUM_STANDARD_TARGETS)
+ return NULL;
+
+ return ebt_standard_targets[num];
+}
+
+static inline int ebt_fill_target(const char *str, unsigned int *verdict)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < NUM_STANDARD_TARGETS; i++) {
+ if (!strcmp(str, nft_ebt_standard_target(i))) {
+ *verdict = -i - 1;
+ break;
+ }
+ }
+
+ if (i == NUM_STANDARD_TARGETS)
+ ret = 1;
+
+ return ret;
+}
+
+static inline const char *ebt_target_name(unsigned int verdict)
+{
+ return nft_ebt_standard_target(-verdict - 1);
+}
+
+#define EBT_CHECK_OPTION(flags, mask) ({ \
+ if (*flags & mask) \
+ xtables_error(PARAMETER_PROBLEM, \
+ "Multiple use of same " \
+ "option not allowed"); \
+ *flags |= mask; \
+}) \
+
+void ebt_cs_clean(struct ebtables_command_state *cs);
+
+#endif
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
new file mode 100644
index 00000000..52b1bed2
--- /dev/null
+++ b/iptables/nft-ipv4.c
@@ -0,0 +1,517 @@
+/*
+ * (C) 2012-2014 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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 <http://www.sophos.com>
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netdb.h>
+
+#include <xtables.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "nft.h"
+#include "nft-shared.h"
+
+static int nft_ipv4_add(struct nftnl_rule *r, void *data)
+{
+ struct iptables_command_state *cs = data;
+ struct xtables_rule_match *matchp;
+ uint32_t op;
+ int ret;
+
+ if (cs->fw.ip.iniface[0] != '\0') {
+ op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_IN);
+ add_iniface(r, cs->fw.ip.iniface, op);
+ }
+
+ if (cs->fw.ip.outiface[0] != '\0') {
+ op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_OUT);
+ add_outiface(r, cs->fw.ip.outiface, op);
+ }
+
+ if (cs->fw.ip.proto != 0) {
+ op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO);
+ add_proto(r, offsetof(struct iphdr, protocol), 1,
+ cs->fw.ip.proto, op);
+ }
+
+ if (cs->fw.ip.src.s_addr != 0) {
+ op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP);
+ add_addr(r, offsetof(struct iphdr, saddr),
+ &cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr,
+ sizeof(struct in_addr), op);
+ }
+ if (cs->fw.ip.dst.s_addr != 0) {
+ op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP);
+ add_addr(r, offsetof(struct iphdr, daddr),
+ &cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr,
+ sizeof(struct in_addr), op);
+ }
+ if (cs->fw.ip.flags & IPT_F_FRAG) {
+ add_payload(r, offsetof(struct iphdr, frag_off), 2,
+ NFT_PAYLOAD_NETWORK_HEADER);
+ /* get the 13 bits that contain the fragment offset */
+ add_bitwise_u16(r, 0x1fff, !0x1fff);
+
+ /* if offset is non-zero, this is a fragment */
+ op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_FRAG);
+ add_cmp_u16(r, 0, op);
+ }
+
+ add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags);
+
+ for (matchp = cs->matches; matchp; matchp = matchp->next) {
+ /* Use nft built-in comments support instead of comment match */
+ if (strcmp(matchp->match->name, "comment") == 0) {
+ ret = add_comment(r, (char *)matchp->match->m->data);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = add_match(r, matchp->match->m);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ /* 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 & IPT_F_GOTO));
+}
+
+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
+ || 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_xt_ctx *ctx, struct nftnl_expr *e, bool *inv)
+{
+ uint8_t op;
+
+ /* we assume correct mask and xor */
+ if (!(ctx->flags & NFT_XT_CTX_BITWISE))
+ return;
+
+ /* we assume correct data */
+ op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+ if (op == NFT_CMP_EQ)
+ *inv = true;
+ else
+ *inv = false;
+
+ ctx->flags &= ~NFT_XT_CTX_BITWISE;
+}
+
+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_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ parse_meta(e, ctx->meta.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 parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
+{
+ mask->s_addr = ctx->bitwise.mask[0];
+}
+
+static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e, void *data)
+{
+ struct iptables_command_state *cs = data;
+ struct in_addr addr;
+ uint8_t proto;
+ bool inv;
+
+ switch(ctx->payload.offset) {
+ case offsetof(struct iphdr, saddr):
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ cs->fw.ip.src.s_addr = addr.s_addr;
+ if (ctx->flags & NFT_XT_CTX_BITWISE) {
+ parse_mask_ipv4(ctx, &cs->fw.ip.smsk);
+ ctx->flags &= ~NFT_XT_CTX_BITWISE;
+ } else {
+ 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(e, &addr, sizeof(addr), &inv);
+ cs->fw.ip.dst.s_addr = addr.s_addr;
+ if (ctx->flags & NFT_XT_CTX_BITWISE) {
+ parse_mask_ipv4(ctx, &cs->fw.ip.dmsk);
+ ctx->flags &= ~NFT_XT_CTX_BITWISE;
+ } else {
+ 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(e, &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(ctx, e, &inv);
+ if (inv)
+ cs->fw.ip.invflags |= IPT_INV_FRAG;
+ break;
+ default:
+ DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
+ break;
+ }
+}
+
+static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ cs->jumpto = jumpto;
+
+ if (nft_goto)
+ cs->fw.ip.flags |= IPT_F_GOTO;
+}
+
+static void nft_ipv4_print_header(unsigned int format, const char *chain,
+ const char *pol,
+ const struct xt_counters *counters,
+ bool basechain, uint32_t refs)
+{
+ print_header(format, chain, pol, counters, basechain, refs);
+}
+
+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_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 nftnl_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct iptables_command_state cs = {};
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ print_firewall_details(&cs, cs.jumpto, cs.fw.ip.flags,
+ cs.fw.ip.invflags, cs.fw.ip.proto,
+ 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)
+ fputs(" ", stdout);
+
+#ifdef IPT_F_GOTO
+ if (cs.fw.ip.flags & IPT_F_GOTO)
+ printf("[goto] ");
+#endif
+
+ 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 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);
+
+ 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);
+
+ 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,
+ 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.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,
+ 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");
+}
+
+static void nft_ipv4_parse_target(struct xtables_target *t, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ cs->target = t;
+}
+
+static bool nft_ipv4_rule_find(struct nft_family_ops *ops,
+ struct nftnl_rule *r, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ return nft_ipv46_rule_find(ops, r, cs);
+}
+
+static void nft_ipv4_save_counters(const void *data)
+{
+ const struct iptables_command_state *cs = data;
+
+ save_counters(cs->counters.pcnt, cs->counters.bcnt);
+}
+
+static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl)
+{
+ const struct iptables_command_state *cs = data;
+ const char *comment;
+ int ret;
+
+ xlate_ifname(xl, "iifname", cs->fw.ip.iniface,
+ cs->fw.ip.invflags & IPT_INV_VIA_IN);
+ xlate_ifname(xl, "oifname", cs->fw.ip.outiface,
+ cs->fw.ip.invflags & IPT_INV_VIA_OUT);
+
+ if (cs->fw.ip.flags & IPT_F_FRAG) {
+ xt_xlate_add(xl, "ip frag-off %s%x ",
+ cs->fw.ip.invflags & IPT_INV_FRAG? "" : "!= ", 0);
+ }
+
+ if (cs->fw.ip.proto != 0) {
+ const struct protoent *pent =
+ getprotobynumber(cs->fw.ip.proto);
+ char protonum[strlen("255") + 1];
+
+ if (!xlate_find_match(cs, pent->p_name)) {
+ snprintf(protonum, sizeof(protonum), "%u",
+ cs->fw.ip.proto);
+ protonum[sizeof(protonum) - 1] = '\0';
+ xt_xlate_add(xl, "ip protocol %s%s ",
+ cs->fw.ip.invflags & IPT_INV_PROTO ?
+ "!= " : "",
+ pent ? pent->p_name : protonum);
+ }
+ }
+
+ if (cs->fw.ip.src.s_addr != 0) {
+ xt_xlate_add(xl, "ip saddr %s%s%s ",
+ cs->fw.ip.invflags & IPT_INV_SRCIP ? "!= " : "",
+ inet_ntoa(cs->fw.ip.src),
+ xtables_ipmask_to_numeric(&cs->fw.ip.smsk));
+ }
+ if (cs->fw.ip.dst.s_addr != 0) {
+ xt_xlate_add(xl, "ip daddr %s%s%s ",
+ cs->fw.ip.invflags & IPT_INV_DSTIP ? "!= " : "",
+ inet_ntoa(cs->fw.ip.dst),
+ xtables_ipmask_to_numeric(&cs->fw.ip.dmsk));
+ }
+
+ ret = xlate_matches(cs, xl);
+ if (!ret)
+ return ret;
+
+ /* Always add counters per rule, as in iptables */
+ xt_xlate_add(xl, "counter ");
+
+ comment = xt_xlate_get_comment(xl);
+ if (comment)
+ xt_xlate_add(xl, "comment %s", comment);
+
+ ret = xlate_action(cs, !!(cs->fw.ip.flags & IPT_F_GOTO), xl);
+
+ return ret;
+}
+
+struct nft_family_ops nft_family_ops_ipv4 = {
+ .add = nft_ipv4_add,
+ .is_same = nft_ipv4_is_same,
+ .parse_meta = nft_ipv4_parse_meta,
+ .parse_payload = nft_ipv4_parse_payload,
+ .parse_immediate = nft_ipv4_parse_immediate,
+ .print_header = nft_ipv4_print_header,
+ .print_firewall = nft_ipv4_print_firewall,
+ .save_firewall = nft_ipv4_save_firewall,
+ .save_counters = nft_ipv4_save_counters,
+ .proto_parse = nft_ipv4_proto_parse,
+ .post_parse = nft_ipv4_post_parse,
+ .parse_target = nft_ipv4_parse_target,
+ .rule_find = nft_ipv4_rule_find,
+ .xlate = nft_ipv4_xlate,
+};
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
new file mode 100644
index 00000000..c475b8e9
--- /dev/null
+++ b/iptables/nft-ipv6.c
@@ -0,0 +1,466 @@
+/*
+ * (C) 2012-2014 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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 <http://www.sophos.com>
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/ip6.h>
+#include <netdb.h>
+
+#include <xtables.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include "nft.h"
+#include "nft-shared.h"
+
+static int nft_ipv6_add(struct nftnl_rule *r, void *data)
+{
+ struct iptables_command_state *cs = data;
+ struct xtables_rule_match *matchp;
+ uint32_t op;
+ int ret;
+
+ if (cs->fw6.ipv6.iniface[0] != '\0') {
+ op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_IN);
+ add_iniface(r, cs->fw6.ipv6.iniface, op);
+ }
+
+ if (cs->fw6.ipv6.outiface[0] != '\0') {
+ op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_OUT);
+ add_outiface(r, cs->fw6.ipv6.outiface, op);
+ }
+
+ if (cs->fw6.ipv6.proto != 0) {
+ op = nft_invflags2cmp(cs->fw6.ipv6.invflags, XT_INV_PROTO);
+ add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1,
+ cs->fw6.ipv6.proto, op);
+ }
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)) {
+ op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_SRCIP);
+ add_addr(r, offsetof(struct ip6_hdr, ip6_src),
+ &cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
+ sizeof(struct in6_addr), op);
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)) {
+ op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_DSTIP);
+ add_addr(r, offsetof(struct ip6_hdr, ip6_dst),
+ &cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
+ sizeof(struct in6_addr), op);
+ }
+ add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags);
+
+ for (matchp = cs->matches; matchp; matchp = matchp->next) {
+ /* Use nft built-in comments support instead of comment match */
+ if (strcmp(matchp->match->name, "comment") == 0) {
+ ret = add_comment(r, (char *)matchp->match->m->data);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = add_match(r, matchp->match->m);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ /* 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 & IP6T_F_GOTO));
+}
+
+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,
+ 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_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ parse_meta(e, ctx->meta.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 parse_mask_ipv6(struct nft_xt_ctx *ctx, struct in6_addr *mask)
+{
+ memcpy(mask, ctx->bitwise.mask, sizeof(struct in6_addr));
+}
+
+static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e, void *data)
+{
+ struct iptables_command_state *cs = data;
+ struct in6_addr addr;
+ uint8_t proto;
+ bool inv;
+
+ switch (ctx->payload.offset) {
+ case offsetof(struct ip6_hdr, ip6_src):
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr));
+ if (ctx->flags & NFT_XT_CTX_BITWISE) {
+ parse_mask_ipv6(ctx, &cs->fw6.ipv6.smsk);
+ ctx->flags &= ~NFT_XT_CTX_BITWISE;
+ } else {
+ memset(&cs->fw.ip.smsk, 0xff, sizeof(struct in6_addr));
+ }
+
+ if (inv)
+ cs->fw6.ipv6.invflags |= IP6T_INV_SRCIP;
+ break;
+ case offsetof(struct ip6_hdr, ip6_dst):
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr));
+ if (ctx->flags & NFT_XT_CTX_BITWISE) {
+ parse_mask_ipv6(ctx, &cs->fw6.ipv6.dmsk);
+ ctx->flags &= ~NFT_XT_CTX_BITWISE;
+ } else {
+ memset(&cs->fw.ip.dmsk, 0xff, sizeof(struct in6_addr));
+ }
+
+ if (inv)
+ cs->fw6.ipv6.invflags |= IP6T_INV_DSTIP;
+ break;
+ case offsetof(struct ip6_hdr, ip6_nxt):
+ get_cmp_data(e, &proto, sizeof(proto), &inv);
+ cs->fw6.ipv6.flags |= IP6T_F_PROTO;
+ cs->fw6.ipv6.proto = proto;
+ if (inv)
+ cs->fw6.ipv6.invflags |= IP6T_INV_PROTO;
+ default:
+ DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
+ break;
+ }
+}
+
+static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ cs->jumpto = jumpto;
+
+ if (nft_goto)
+ cs->fw6.ipv6.flags |= IP6T_F_GOTO;
+}
+
+static void nft_ipv6_print_header(unsigned int format, const char *chain,
+ const char *pol,
+ const struct xt_counters *counters,
+ bool basechain, uint32_t refs)
+{
+ print_header(format, chain, pol, counters, basechain, refs);
+}
+
+static void print_ipv6_addr(const struct iptables_command_state *cs,
+ unsigned int format)
+{
+ char buf[BUFSIZ];
+
+ fputc(cs->fw6.ipv6.invflags & IP6T_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 & IP6T_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 nft_ipv6_print_firewall(struct nftnl_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct iptables_command_state cs = {};
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ print_firewall_details(&cs, cs.jumpto, cs.fw6.ipv6.flags,
+ cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto,
+ 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)
+ fputs(" ", stdout);
+
+ if (cs.fw6.ipv6.flags & IP6T_F_GOTO)
+ printf("[goto] ");
+
+ 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 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);
+
+ save_ipv6_addr('s', &cs->fw6.ipv6.src,
+ cs->fw6.ipv6.invflags & IP6T_INV_SRCIP);
+ save_ipv6_addr('d', &cs->fw6.ipv6.dst,
+ cs->fw6.ipv6.invflags & IP6T_INV_DSTIP);
+
+ 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 */
+static int is_exthdr(uint16_t proto)
+{
+ return (proto == IPPROTO_ROUTING ||
+ proto == IPPROTO_FRAGMENT ||
+ proto == IPPROTO_AH ||
+ proto == IPPROTO_DSTOPTS);
+}
+
+static void nft_ipv6_proto_parse(struct iptables_command_state *cs,
+ struct xtables_args *args)
+{
+ cs->fw6.ipv6.proto = args->proto;
+ cs->fw6.ipv6.invflags = args->invflags;
+
+ 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);
+}
+
+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;
+ /* 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,
+ 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");
+}
+
+static void nft_ipv6_parse_target(struct xtables_target *t, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ cs->target = t;
+}
+
+static bool nft_ipv6_rule_find(struct nft_family_ops *ops,
+ struct nftnl_rule *r, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ return nft_ipv46_rule_find(ops, r, cs);
+}
+
+static void nft_ipv6_save_counters(const void *data)
+{
+ const struct iptables_command_state *cs = data;
+
+ save_counters(cs->counters.pcnt, cs->counters.bcnt);
+}
+
+static void xlate_ipv6_addr(const char *selector, const struct in6_addr *addr,
+ const struct in6_addr *mask,
+ int invert, struct xt_xlate *xl)
+{
+ char addr_str[INET6_ADDRSTRLEN];
+
+ if (!invert && IN6_IS_ADDR_UNSPECIFIED(addr))
+ return;
+
+ inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
+ xt_xlate_add(xl, "%s %s%s%s ", selector, invert ? "!= " : "", addr_str,
+ xtables_ip6mask_to_numeric(mask));
+}
+
+static int nft_ipv6_xlate(const void *data, struct xt_xlate *xl)
+{
+ const struct iptables_command_state *cs = data;
+ const char *comment;
+ int ret;
+
+ xlate_ifname(xl, "iifname", cs->fw6.ipv6.iniface,
+ cs->fw6.ipv6.invflags & IP6T_INV_VIA_IN);
+ xlate_ifname(xl, "oifname", cs->fw6.ipv6.outiface,
+ cs->fw6.ipv6.invflags & IP6T_INV_VIA_OUT);
+
+ if (cs->fw6.ipv6.proto != 0) {
+ const struct protoent *pent =
+ getprotobynumber(cs->fw6.ipv6.proto);
+ char protonum[strlen("255") + 1];
+
+ if (!xlate_find_match(cs, pent->p_name)) {
+ snprintf(protonum, sizeof(protonum), "%u",
+ cs->fw6.ipv6.proto);
+ protonum[sizeof(protonum) - 1] = '\0';
+ xt_xlate_add(xl, "meta l4proto %s%s ",
+ cs->fw6.ipv6.invflags & IP6T_INV_PROTO ?
+ "!= " : "",
+ pent ? pent->p_name : protonum);
+ }
+ }
+
+ xlate_ipv6_addr("ip6 saddr", &cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
+ cs->fw6.ipv6.invflags & IP6T_INV_SRCIP, xl);
+ xlate_ipv6_addr("ip6 daddr", &cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
+ cs->fw6.ipv6.invflags & IP6T_INV_DSTIP, xl);
+
+ ret = xlate_matches(cs, xl);
+ if (!ret)
+ return ret;
+
+ /* Always add counters per rule, as in iptables */
+ xt_xlate_add(xl, "counter ");
+
+ comment = xt_xlate_get_comment(xl);
+ if (comment)
+ xt_xlate_add(xl, "comment %s", comment);
+
+ ret = xlate_action(cs, !!(cs->fw6.ipv6.flags & IP6T_F_GOTO), xl);
+
+ return ret;
+}
+
+struct nft_family_ops nft_family_ops_ipv6 = {
+ .add = nft_ipv6_add,
+ .is_same = nft_ipv6_is_same,
+ .parse_meta = nft_ipv6_parse_meta,
+ .parse_payload = nft_ipv6_parse_payload,
+ .parse_immediate = nft_ipv6_parse_immediate,
+ .print_header = nft_ipv6_print_header,
+ .print_firewall = nft_ipv6_print_firewall,
+ .save_firewall = nft_ipv6_save_firewall,
+ .save_counters = nft_ipv6_save_counters,
+ .proto_parse = nft_ipv6_proto_parse,
+ .post_parse = nft_ipv6_post_parse,
+ .parse_target = nft_ipv6_parse_target,
+ .rule_find = nft_ipv6_rule_find,
+ .xlate = nft_ipv6_xlate,
+};
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
new file mode 100644
index 00000000..68e5c55d
--- /dev/null
+++ b/iptables/nft-shared.c
@@ -0,0 +1,883 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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 <http://www.sophos.com>
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include <xtables.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "nft-shared.h"
+#include "nft-bridge.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;
+extern struct nft_family_ops nft_family_ops_arp;
+extern struct nft_family_ops nft_family_ops_bridge;
+
+void add_meta(struct nftnl_rule *r, uint32_t key)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("meta");
+ if (expr == NULL)
+ return;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, key);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
+
+ nftnl_rule_add_expr(r, expr);
+}
+
+void add_payload(struct nftnl_rule *r, int offset, int len, uint32_t base)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("payload");
+ if (expr == NULL)
+ return;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, NFT_REG_1);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len);
+
+ nftnl_rule_add_expr(r, expr);
+}
+
+/* bitwise operation is = sreg & mask ^ xor */
+void add_bitwise_u16(struct nftnl_rule *r, int mask, int xor)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("bitwise");
+ if (expr == NULL)
+ return;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, NFT_REG_1);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, NFT_REG_1);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, sizeof(uint16_t));
+ nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t));
+ nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t));
+
+ nftnl_rule_add_expr(r, expr);
+}
+
+static void add_bitwise(struct nftnl_rule *r, uint8_t *mask, size_t len)
+{
+ struct nftnl_expr *expr;
+ uint32_t xor[4] = { 0 };
+
+ expr = nftnl_expr_alloc("bitwise");
+ if (expr == NULL)
+ return;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, NFT_REG_1);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, NFT_REG_1);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len);
+ nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len);
+ nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, len);
+
+ nftnl_rule_add_expr(r, expr);
+}
+
+void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("cmp");
+ if (expr == NULL)
+ return;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_SREG, NFT_REG_1);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_OP, op);
+ nftnl_expr_set(expr, NFTNL_EXPR_CMP_DATA, data, len);
+
+ nftnl_rule_add_expr(r, expr);
+}
+
+void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op)
+{
+ add_cmp_ptr(r, op, &val, sizeof(val));
+}
+
+void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op)
+{
+ add_cmp_ptr(r, op, &val, sizeof(val));
+}
+
+void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op)
+{
+ add_cmp_ptr(r, op, &val, sizeof(val));
+}
+
+void add_iniface(struct nftnl_rule *r, char *iface, uint32_t op)
+{
+ int iface_len;
+
+ iface_len = strlen(iface);
+
+ add_meta(r, NFT_META_IIFNAME);
+ if (iface[iface_len - 1] == '+')
+ add_cmp_ptr(r, op, iface, iface_len - 1);
+ else
+ add_cmp_ptr(r, op, iface, iface_len + 1);
+}
+
+void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op)
+{
+ int iface_len;
+
+ iface_len = strlen(iface);
+
+ add_meta(r, NFT_META_OIFNAME);
+ if (iface[iface_len - 1] == '+')
+ add_cmp_ptr(r, op, iface, iface_len - 1);
+ else
+ add_cmp_ptr(r, op, iface, iface_len + 1);
+}
+
+void add_addr(struct nftnl_rule *r, int offset,
+ void *data, void *mask, size_t len, uint32_t op)
+{
+ add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER);
+ add_bitwise(r, mask, len);
+
+ add_cmp_ptr(r, op, data, len);
+}
+
+void add_proto(struct nftnl_rule *r, int offset, size_t len,
+ uint8_t proto, uint32_t op)
+{
+ add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER);
+ add_cmp_u8(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;
+}
+
+int parse_meta(struct nftnl_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;
+ uint32_t len;
+
+ switch(key) {
+ case NFT_META_IIF:
+ value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
+ if (nftnl_expr_get_u32(e, NFTNL_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 = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
+ if (nftnl_expr_get_u32(e, NFTNL_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 = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_IN;
+
+ memcpy(iniface, ifname, len);
+
+ if (iniface[len] == '\0')
+ memset(iniface_mask, 0xff, len);
+ else {
+ iniface[len] = '+';
+ iniface[len+1] = '\0';
+ memset(iniface_mask, 0xff, len + 1);
+ }
+ break;
+ case NFT_META_OIFNAME:
+ ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_OUT;
+
+ memcpy(outiface, ifname, len);
+
+ if (outiface[len] == '\0')
+ memset(outiface_mask, 0xff, len);
+ else {
+ outiface[len] = '+';
+ outiface[len+1] = '\0';
+ memset(outiface_mask, 0xff, len + 1);
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static void *nft_get_data(struct nft_xt_ctx *ctx)
+{
+ switch(ctx->family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ return ctx->state.cs;
+ case NFPROTO_ARP:
+ return ctx->state.cs_arp;
+ case NFPROTO_BRIDGE:
+ return ctx->state.cs_eb;
+ default:
+ /* Should not happen */
+ return NULL;
+ }
+}
+
+void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ uint32_t tg_len;
+ const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
+ const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
+ struct xtables_target *target;
+ struct xt_entry_target *t;
+ size_t size;
+ struct nft_family_ops *ops;
+ void *data = nft_get_data(ctx);
+
+ target = xtables_find_target(targname, XTF_TRY_LOAD);
+ if (target == NULL)
+ return;
+
+ 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 = size;
+ t->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
+ strcpy(t->u.user.name, target->name);
+
+ target->t = t;
+
+ ops = nft_family_ops_lookup(ctx->family);
+ ops->parse_target(target, data);
+}
+
+void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ uint32_t mt_len;
+ const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
+ const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
+ struct xtables_match *match;
+ struct xtables_rule_match **matches;
+ struct xt_entry_match *m;
+ struct nft_family_ops *ops;
+
+ switch (ctx->family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ matches = &ctx->state.cs->matches;
+ break;
+ case NFPROTO_BRIDGE:
+ matches = &ctx->state.cs_eb->matches;
+ break;
+ default:
+ fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
+ ctx->family);
+ exit(EXIT_FAILURE);
+ }
+
+ match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
+ if (match == NULL)
+ return;
+
+ m = calloc(1, sizeof(struct xt_entry_match) + mt_len);
+ if (m == NULL) {
+ fprintf(stderr, "OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ memcpy(&m->data, mt_info, mt_len);
+ m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
+ m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
+ strcpy(m->u.user.name, match->name);
+
+ match->m = m;
+
+ ops = nft_family_ops_lookup(ctx->family);
+ if (ops->parse_match != NULL)
+ ops->parse_match(match, nft_get_data(ctx));
+}
+
+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 nftnl_expr *e, void *data, size_t dlen, bool *inv)
+{
+ uint32_t len;
+ uint8_t op;
+
+ memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen);
+ op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+ if (op == NFT_CMP_NEQ)
+ *inv = true;
+ else
+ *inv = false;
+}
+
+void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
+ ctx->meta.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
+ ctx->flags |= NFT_XT_CTX_META;
+}
+
+void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
+ ctx->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
+ ctx->flags |= NFT_XT_CTX_PAYLOAD;
+}
+
+void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ uint32_t reg, len;
+ const void *data;
+
+ reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
+ if (ctx->reg && reg != ctx->reg)
+ return;
+
+ data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
+ memcpy(ctx->bitwise.xor, data, len);
+ data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
+ memcpy(ctx->bitwise.mask, data, len);
+ ctx->flags |= NFT_XT_CTX_BITWISE;
+}
+
+void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ struct nft_family_ops *ops = nft_family_ops_lookup(ctx->family);
+ void *data = nft_get_data(ctx);
+ uint32_t reg;
+
+ reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
+ if (ctx->reg && reg != ctx->reg)
+ return;
+
+ if (ctx->flags & NFT_XT_CTX_META) {
+ ops->parse_meta(ctx, e, data);
+ ctx->flags &= ~NFT_XT_CTX_META;
+ }
+ /* bitwise context is interpreted from payload */
+ if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
+ ops->parse_payload(ctx, e, data);
+ ctx->flags &= ~NFT_XT_CTX_PAYLOAD;
+ }
+}
+
+void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
+{
+ counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
+ counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
+}
+
+void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ int verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
+ const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
+ struct nft_family_ops *ops;
+ const char *jumpto = NULL;
+ bool nft_goto = false;
+ void *data = nft_get_data(ctx);
+
+ /* Standard target? */
+ switch(verdict) {
+ case NF_ACCEPT:
+ jumpto = "ACCEPT";
+ break;
+ case NF_DROP:
+ jumpto = "DROP";
+ break;
+ case NFT_RETURN:
+ jumpto = "RETURN";
+ break;;
+ case NFT_GOTO:
+ nft_goto = true;
+ case NFT_JUMP:
+ jumpto = chain;
+ break;
+ }
+
+ ops = nft_family_ops_lookup(ctx->family);
+ ops->parse_immediate(jumpto, nft_goto, data);
+}
+
+void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
+ struct iptables_command_state *cs)
+{
+ struct nftnl_expr_iter *iter;
+ struct nftnl_expr *expr;
+ int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
+ struct nft_xt_ctx ctx = {
+ .state.cs = cs,
+ .family = family,
+ };
+
+ iter = nftnl_expr_iter_create(r);
+ if (iter == NULL)
+ return;
+
+ ctx.iter = iter;
+ expr = nftnl_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+
+ if (strcmp(name, "counter") == 0)
+ nft_parse_counter(expr, &ctx.state.cs->counters);
+ else if (strcmp(name, "payload") == 0)
+ nft_parse_payload(&ctx, expr);
+ else if (strcmp(name, "meta") == 0)
+ nft_parse_meta(&ctx, expr);
+ else if (strcmp(name, "bitwise") == 0)
+ nft_parse_bitwise(&ctx, expr);
+ else if (strcmp(name, "cmp") == 0)
+ nft_parse_cmp(&ctx, expr);
+ else if (strcmp(name, "immediate") == 0)
+ nft_parse_immediate(&ctx, expr);
+ else if (strcmp(name, "match") == 0)
+ nft_parse_match(&ctx, expr);
+ else if (strcmp(name, "target") == 0)
+ nft_parse_target(&ctx, expr);
+
+ expr = nftnl_expr_iter_next(iter);
+ }
+
+ nftnl_expr_iter_destroy(iter);
+
+ if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
+ const void *data;
+ uint32_t len;
+ struct xtables_match *match;
+ struct xt_entry_match *m;
+
+ data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
+ match = xtables_find_match("comment", XTF_TRY_LOAD,
+ &cs->matches);
+ if (match == NULL)
+ return;
+
+ m = calloc(1, sizeof(struct xt_entry_match) + len);
+ if (m == NULL) {
+ fprintf(stderr, "OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ memcpy(&m->data, get_comment(data, len), len);
+ m->u.match_size = len + XT_ALIGN(sizeof(struct xt_entry_match));
+ m->u.user.revision = 0;
+ strcpy(m->u.user.name, match->name);
+
+ match->m = m;
+ }
+
+ 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_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);
+ xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
+ fputs("packets, ", stdout);
+ xtables_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");
+}
+
+void print_firewall_details(const struct iptables_command_state *cs,
+ const char *targname, uint8_t flags,
+ uint8_t invflags, uint8_t proto,
+ unsigned int num, unsigned int format)
+{
+ if (format & FMT_LINENUMBERS)
+ printf(FMT("%-4u ", "%u "), num);
+
+ if (!(format & FMT_NOCOUNTS)) {
+ xtables_print_num(cs->counters.pcnt, format);
+ xtables_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);
+ }
+}
+
+void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
+ unsigned int format)
+{
+ char iface[IFNAMSIZ+2];
+
+ if (!(format & FMT_VIA))
+ return;
+
+ 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);
+}
+
+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)
+{
+ 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 save_counters(uint64_t pcnt, uint64_t bcnt)
+{
+ printf("[%llu:%llu] ", (unsigned long long)pcnt,
+ (unsigned long long)bcnt);
+}
+
+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);
+ }
+}
+
+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(&cs->fw, matchp->match->m,
+ format & FMT_NUMERIC);
+ }
+ }
+
+ if (cs->target != NULL) {
+ if (cs->target->print != NULL) {
+ cs->target->print(&cs->fw, cs->target->t,
+ format & FMT_NUMERIC);
+ }
+ }
+}
+
+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;
+ case NFPROTO_ARP:
+ return &nft_family_ops_arp;
+ case NFPROTO_BRIDGE:
+ return &nft_family_ops_bridge;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+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,
+ mp1->match->userspacesize) != 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->userspacesize) != 0)
+ return false;
+
+ return true;
+}
+
+bool nft_ipv46_rule_find(struct nft_family_ops *ops,
+ struct nftnl_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
new file mode 100644
index 00000000..c0948fd4
--- /dev/null
+++ b/iptables/nft-shared.h
@@ -0,0 +1,272 @@
+#ifndef _NFT_SHARED_H_
+#define _NFT_SHARED_H_
+
+#include <stdbool.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include <linux/netfilter_arp/arp_tables.h>
+
+#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 xtables_args;
+struct xt_xlate;
+
+enum {
+ NFT_XT_CTX_PAYLOAD = (1 << 0),
+ NFT_XT_CTX_META = (1 << 1),
+ NFT_XT_CTX_BITWISE = (1 << 2),
+};
+
+struct nft_xt_ctx {
+ union {
+ struct iptables_command_state *cs;
+ struct arptables_command_state *cs_arp;
+ struct ebtables_command_state *cs_eb;
+ } state;
+ struct nftnl_expr_iter *iter;
+ int family;
+ uint32_t flags;
+
+ uint32_t reg;
+ struct {
+ uint32_t offset;
+ uint32_t len;
+ } payload;
+ struct {
+ uint32_t key;
+ } meta;
+ struct {
+ uint32_t mask[4];
+ uint32_t xor[4];
+ } bitwise;
+};
+
+struct nft_family_ops {
+ int (*add)(struct nftnl_rule *r, void *data);
+ bool (*is_same)(const void *data_a,
+ const void *data_b);
+ void (*print_payload)(struct nftnl_expr *e,
+ struct nftnl_expr_iter *iter);
+ void (*parse_meta)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ void *data);
+ void (*parse_payload)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ void *data);
+ void (*parse_bitwise)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ void *data);
+ void (*parse_cmp)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ void *data);
+ void (*parse_immediate)(const char *jumpto, bool nft_goto, void *data);
+
+ void (*print_table_header)(const char *tablename);
+ void (*print_header)(unsigned int format, const char *chain,
+ const char *pol,
+ const struct xt_counters *counters, bool basechain,
+ uint32_t refs);
+ void (*print_firewall)(struct nftnl_rule *r, unsigned int num,
+ unsigned int format);
+ void (*save_firewall)(const void *data, unsigned int format);
+ void (*save_counters)(const void *data);
+ 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_match)(struct xtables_match *m, void *data);
+ void (*parse_target)(struct xtables_target *t, void *data);
+ bool (*rule_find)(struct nft_family_ops *ops, struct nftnl_rule *r,
+ void *data);
+ int (*xlate)(const void *data, struct xt_xlate *xl);
+};
+
+void add_meta(struct nftnl_rule *r, uint32_t key);
+void add_payload(struct nftnl_rule *r, int offset, int len, uint32_t base);
+void add_bitwise_u16(struct nftnl_rule *r, int mask, int xor);
+void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len);
+void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op);
+void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op);
+void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op);
+void add_iniface(struct nftnl_rule *r, char *iface, uint32_t op);
+void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op);
+void add_addr(struct nftnl_rule *r, int offset,
+ void *data, void *mask, size_t len, uint32_t op);
+void add_proto(struct nftnl_rule *r, int offset, size_t len,
+ uint8_t proto, uint32_t op);
+void add_compat(struct nftnl_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,
+ 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 parse_meta(struct nftnl_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 nftnl_expr *e, void *data, size_t dlen, bool *inv);
+void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters);
+void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
+ struct iptables_command_state *cs);
+void print_header(unsigned int format, const char *chain, const char *pol,
+ const struct xt_counters *counters, bool basechain,
+ uint32_t refs);
+void print_firewall_details(const struct iptables_command_state *cs,
+ const char *targname, uint8_t flags,
+ uint8_t invflags, uint8_t proto,
+ 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,
+ uint8_t invflags, uint16_t proto,
+ const char *iniface,
+ unsigned const char *iniface_mask,
+ const char *outiface,
+ unsigned const char *outiface_mask);
+void save_counters(uint64_t pcnt, uint64_t bcnt);
+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);
+
+struct nft_handle;
+bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+ struct iptables_command_state *cs);
+
+bool compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2);
+bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
+
+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;
+};
+
+#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
+
+struct nft_xt_cmd_parse {
+ unsigned int command;
+ unsigned int rulenum;
+ char *table;
+ char *chain;
+ char *newname;
+ char *policy;
+ bool restore;
+ int verbose;
+};
+
+void do_parse(struct nft_handle *h, int argc, char *argv[],
+ struct nft_xt_cmd_parse *p, struct iptables_command_state *cs,
+ struct xtables_args *args);
+
+struct nft_xt_restore_parse {
+ FILE *in;
+ int testing;
+ const char *tablename;
+};
+
+struct nftnl_chain_list;
+
+struct nft_xt_restore_cb {
+ void (*table_new)(struct nft_handle *h, const char *table);
+ struct nftnl_chain_list *(*chain_list)(struct nft_handle *h);
+ int (*chains_purge)(struct nft_handle *h, const char *table,
+ struct nftnl_chain_list *clist);
+ void (*chain_del)(struct nftnl_chain_list *clist, const char *curtable,
+ const char *chain);
+ int (*chain_set)(struct nft_handle *h, const char *table,
+ const char *chain, const char *policy,
+ const struct xt_counters *counters);
+ int (*chain_user_add)(struct nft_handle *h, const char *chain,
+ const char *table);
+
+ int (*rule_flush)(struct nft_handle *h, const char *chain, const char *table);
+
+ int (*do_command)(struct nft_handle *h, int argc, char *argv[],
+ char **table, bool restore);
+
+ int (*commit)(struct nft_handle *h);
+ int (*abort)(struct nft_handle *h);
+};
+
+void xtables_restore_parse(struct nft_handle *h,
+ struct nft_xt_restore_parse *p,
+ struct nft_xt_restore_cb *cb,
+ int argc, char *argv[]);
+
+#endif
diff --git a/iptables/nft.c b/iptables/nft.c
new file mode 100644
index 00000000..fee91bc7
--- /dev/null
+++ b/iptables/nft.c
@@ -0,0 +1,2906 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <http://www.sophos.com>
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <netdb.h> /* getprotobynumber */
+#include <time.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#include <xtables.h>
+#include <libiptc/libxtc.h>
+#include <libiptc/xtcshared.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <netinet/ip6.h>
+
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_tables_compat.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/table.h>
+#include <libnftnl/chain.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/set.h>
+#include <libnftnl/udata.h>
+
+#include <netinet/in.h> /* inet_ntoa */
+#include <arpa/inet.h>
+
+#include "nft.h"
+#include "xshared.h" /* proto_to_name */
+#include "nft-shared.h"
+#include "xtables-config-parser.h"
+
+static void *nft_fn;
+
+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)
+ 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 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_nftnl_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_nftnl_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_nftnl_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 void mnl_nftnl_batch_reset(void)
+{
+ struct batch_page *batch_page, *next;
+
+ list_for_each_entry_safe(batch_page, next, &batch_page_list, head) {
+ list_del(&batch_page->head);
+ free(batch_page->batch);
+ free(batch_page);
+ batch_num_pages--;
+ }
+}
+
+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;
+ int i = 0, ret;
+
+ mnl_nft_set_sndbuffer(nl);
+
+ list_for_each_entry(batch_page, &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
+ }
+
+ ret = sendmsg(mnl_socket_get_fd(nl), &msg, 0);
+ mnl_nftnl_batch_reset();
+
+ return ret;
+}
+
+static int mnl_nftnl_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)
+ 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)
+ return -1;
+
+ while (ret > 0 && FD_ISSET(fd, &readfds)) {
+ ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf));
+ if (ret == -1)
+ return -1;
+
+ ret = mnl_cb_run(rcv_buf, ret, 0, h->portid, NULL, NULL);
+ /* Annotate first error and continue, make sure we get all
+ * acknoledgments.
+ */
+ if (!err && ret == -1)
+ err = errno;
+
+ ret = select(fd+1, &readfds, NULL, NULL, &tv);
+ if (ret == -1)
+ return -1;
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ }
+ errno = err;
+ return err ? -1 : 0;
+}
+
+static void mnl_nftnl_batch_begin(struct mnl_nlmsg_batch *batch, uint32_t seq)
+{
+ nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq);
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_nftnl_batch_page_add(batch);
+}
+
+static void mnl_nftnl_batch_end(struct mnl_nlmsg_batch *batch, uint32_t seq)
+{
+ nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq);
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_nftnl_batch_page_add(batch);
+}
+
+enum obj_update_type {
+ NFT_COMPAT_TABLE_ADD,
+ NFT_COMPAT_CHAIN_ADD,
+ NFT_COMPAT_CHAIN_USER_ADD,
+ NFT_COMPAT_CHAIN_USER_DEL,
+ NFT_COMPAT_CHAIN_UPDATE,
+ NFT_COMPAT_CHAIN_RENAME,
+ NFT_COMPAT_RULE_APPEND,
+ NFT_COMPAT_RULE_INSERT,
+ NFT_COMPAT_RULE_REPLACE,
+ NFT_COMPAT_RULE_DELETE,
+ NFT_COMPAT_RULE_FLUSH,
+};
+
+enum obj_action {
+ NFT_COMPAT_COMMIT,
+ NFT_COMPAT_ABORT,
+};
+
+struct obj_update {
+ struct list_head head;
+ enum obj_update_type type;
+ union {
+ struct nftnl_table *table;
+ struct nftnl_chain *chain;
+ struct nftnl_rule *rule;
+ void *ptr;
+ };
+};
+
+static int batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr)
+{
+ struct obj_update *obj;
+
+ obj = calloc(1, sizeof(struct obj_update));
+ if (obj == NULL)
+ return -1;
+
+ obj->ptr = ptr;
+ obj->type = type;
+ list_add_tail(&obj->head, &h->obj_list);
+ h->obj_list_num++;
+
+ return 0;
+}
+
+static int batch_table_add(struct nft_handle *h, enum obj_update_type type,
+ struct nftnl_table *t)
+{
+ return batch_add(h, type, t);
+}
+
+static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
+ struct nftnl_chain *c)
+{
+ return batch_add(h, type, c);
+}
+
+static int batch_rule_add(struct nft_handle *h, enum obj_update_type type,
+ struct nftnl_rule *r)
+{
+ return batch_add(h, type, r);
+}
+
+struct builtin_table xtables_ipv4[TABLES_MAX] = {
+ [RAW] = {
+ .name = "raw",
+ .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,
+ },
+ },
+ },
+ [MANGLE] = {
+ .name = "mangle",
+ .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,
+ },
+ },
+ },
+ [FILTER] = {
+ .name = "filter",
+ .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,
+ },
+ },
+ },
+ [SECURITY] = {
+ .name = "security",
+ .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] = {
+ .name = "nat",
+ .chains = {
+ {
+ .name = "PREROUTING",
+ .type = "nat",
+ .prio = -100, /* NF_IP_PRI_NAT_DST */
+ .hook = NF_INET_PRE_ROUTING,
+ },
+ {
+ .name = "INPUT",
+ .type = "nat",
+ .prio = 100, /* NF_IP_PRI_NAT_SRC */
+ .hook = NF_INET_LOCAL_IN,
+ },
+ {
+ .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,
+ },
+ },
+ },
+};
+
+#include <linux/netfilter_arp.h>
+
+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,
+ },
+ },
+ },
+};
+
+#include <linux/netfilter_bridge.h>
+
+struct builtin_table xtables_bridge[TABLES_MAX] = {
+ [FILTER] = {
+ .name = "filter",
+ .chains = {
+ {
+ .name = "INPUT",
+ .type = "filter",
+ .prio = NF_BR_PRI_FILTER_BRIDGED,
+ .hook = NF_BR_LOCAL_IN,
+ },
+ {
+ .name = "FORWARD",
+ .type = "filter",
+ .prio = NF_BR_PRI_FILTER_BRIDGED,
+ .hook = NF_BR_FORWARD,
+ },
+ {
+ .name = "OUTPUT",
+ .type = "filter",
+ .prio = NF_BR_PRI_FILTER_BRIDGED,
+ .hook = NF_BR_LOCAL_OUT,
+ },
+ },
+ },
+ [NAT] = {
+ .name = "nat",
+ .chains = {
+ {
+ .name = "PREROUTING",
+ .type = "filter",
+ .prio = NF_BR_PRI_NAT_DST_BRIDGED,
+ .hook = NF_BR_PRE_ROUTING,
+ },
+ {
+ .name = "OUTPUT",
+ .type = "filter",
+ .prio = NF_BR_PRI_NAT_DST_OTHER,
+ .hook = NF_BR_LOCAL_OUT,
+ },
+ {
+ .name = "POSTROUTING",
+ .type = "filter",
+ .prio = NF_BR_PRI_NAT_SRC,
+ .hook = NF_BR_POST_ROUTING,
+ },
+ },
+ },
+};
+
+int nft_table_add(struct nft_handle *h, struct nftnl_table *t, uint16_t flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int ret;
+
+ nlh = nftnl_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
+ NLM_F_ACK|flags, h->seq);
+ nftnl_table_nlmsg_build_payload(nlh, t);
+ nftnl_table_free(t);
+
+#ifdef NLDEBUG
+ char tmp[1024];
+
+ nft_table_snprintf(tmp, sizeof(tmp), t, 0, 0);
+ printf("DEBUG: table: %s\n", tmp);
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
+#endif
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+
+ return (ret == 0 || (ret == -1 && errno == EEXIST)) ? 0 : -1;
+}
+
+static int nft_table_builtin_add(struct nft_handle *h,
+ struct builtin_table *_t)
+{
+ struct nftnl_table *t;
+ int ret;
+
+ if (_t->initialized)
+ return 0;
+
+ t = nftnl_table_alloc();
+ if (t == NULL)
+ return -1;
+
+ nftnl_table_set(t, NFTNL_TABLE_NAME, (char *)_t->name);
+
+ if (h->batch_support)
+ ret = batch_table_add(h, NFT_COMPAT_TABLE_ADD, t);
+ else
+ ret = nft_table_add(h, t, NLM_F_EXCL);
+
+ if (ret == 0)
+ _t->initialized = true;
+
+ return ret;
+}
+
+static struct nftnl_chain *
+nft_chain_builtin_alloc(struct builtin_table *table,
+ struct builtin_chain *chain, int policy)
+{
+ struct nftnl_chain *c;
+
+ c = nftnl_chain_alloc();
+ if (c == NULL)
+ return NULL;
+
+ nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table->name);
+ nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain->name);
+ nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, chain->hook);
+ nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, chain->prio);
+ nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, policy);
+ nftnl_chain_set(c, NFTNL_CHAIN_TYPE, (char *)chain->type);
+
+ return c;
+}
+
+int nft_chain_add(struct nft_handle *h, struct nftnl_chain *c, uint16_t flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ /* NLM_F_CREATE requests module autoloading */
+ nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
+ NLM_F_ACK|flags|NLM_F_CREATE,
+ h->seq);
+ nftnl_chain_nlmsg_build_payload(nlh, c);
+ nftnl_chain_free(c);
+
+#ifdef NLDEBUG
+ char tmp[1024];
+
+ nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
+ printf("DEBUG: chain: %s\n", tmp);
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
+#endif
+
+ return mnl_talk(h, nlh, NULL, NULL);
+}
+
+static void nft_chain_builtin_add(struct nft_handle *h,
+ struct builtin_table *table,
+ struct builtin_chain *chain)
+{
+ struct nftnl_chain *c;
+
+ c = nft_chain_builtin_alloc(table, chain, NF_ACCEPT);
+ if (c == NULL)
+ return;
+
+ if (h->batch_support)
+ batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
+ else
+ nft_chain_add(h, c, NLM_F_EXCL);
+}
+
+/* find if built-in table already exists */
+static struct builtin_table *
+nft_table_builtin_find(struct nft_handle *h, const char *table)
+{
+ int i;
+ bool found = false;
+
+ for (i=0; i<TABLES_MAX; i++) {
+ if (h->tables[i].name == NULL)
+ continue;
+
+ if (strcmp(h->tables[i].name, table) != 0)
+ continue;
+
+ found = true;
+ break;
+ }
+
+ return found ? &h->tables[i] : NULL;
+}
+
+/* find if built-in chain already exists */
+static struct builtin_chain *
+nft_chain_builtin_find(struct builtin_table *t, const char *chain)
+{
+ int i;
+ bool found = false;
+
+ for (i=0; i<NF_IP_NUMHOOKS && t->chains[i].name != NULL; i++) {
+ if (strcmp(t->chains[i].name, chain) != 0)
+ continue;
+
+ found = true;
+ break;
+ }
+ return found ? &t->chains[i] : NULL;
+}
+
+static void nft_chain_builtin_init(struct nft_handle *h,
+ struct builtin_table *table)
+{
+ int i;
+ struct nftnl_chain_list *list = nft_chain_dump(h);
+ struct nftnl_chain *c;
+
+ /* Initialize built-in chains if they don't exist yet */
+ for (i=0; i<NF_IP_NUMHOOKS && table->chains[i].name != NULL; i++) {
+
+ c = nft_chain_list_find(list, table->name,
+ table->chains[i].name);
+ if (c != NULL)
+ continue;
+
+ nft_chain_builtin_add(h, table, &table->chains[i]);
+ }
+
+ nftnl_chain_list_free(list);
+}
+
+static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
+{
+ int ret = 0;
+ struct builtin_table *t;
+
+ t = nft_table_builtin_find(h, table);
+ if (t == NULL) {
+ ret = -1;
+ goto out;
+ }
+ if (nft_table_builtin_add(h, t) < 0) {
+ /* Built-in table already initialized, skip. */
+ if (errno == EEXIST)
+ goto out;
+ }
+ nft_chain_builtin_init(h, t);
+out:
+ return ret;
+}
+
+static bool nft_chain_builtin(struct nftnl_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 nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) != NULL;
+}
+
+static bool mnl_batch_supported(struct nft_handle *h)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t seq = 1;
+ int ret;
+
+ mnl_nftnl_batch_begin(h->batch, seq++);
+
+ nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ NFT_MSG_NEWSET, AF_INET,
+ NLM_F_ACK, seq++);
+ mnl_nlmsg_batch_next(h->batch);
+
+ mnl_nftnl_batch_end(h->batch, seq++);
+
+ ret = mnl_socket_sendto(h->nl, mnl_nlmsg_batch_head(h->batch),
+ mnl_nlmsg_batch_size(h->batch));
+ if (ret < 0)
+ goto err;
+
+ mnl_nlmsg_batch_reset(h->batch);
+
+ ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(h->nl),
+ NULL, NULL);
+ if (ret <= 0)
+ break;
+
+ ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+ }
+
+ /* We're sending an incomplete message to see if the kernel supports
+ * set messages in batches. EINVAL means that we sent an incomplete
+ * message with missing attributes. The kernel just ignores messages
+ * that we cannot include in the batch.
+ */
+ return (ret == -1 && errno == EINVAL) ? true : false;
+err:
+ mnl_nlmsg_batch_reset(h->batch);
+ return ret;
+}
+
+int nft_init(struct nft_handle *h, struct builtin_table *t)
+{
+ h->nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (h->nl == NULL)
+ return -1;
+
+ if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0)
+ return -1;
+
+ h->portid = mnl_socket_get_portid(h->nl);
+ h->tables = t;
+
+ INIT_LIST_HEAD(&h->obj_list);
+
+ h->batch = mnl_nftnl_batch_alloc();
+ h->batch_support = mnl_batch_supported(h);
+
+ return 0;
+}
+
+static void flush_rule_cache(struct nft_handle *h)
+{
+ if (!h->rule_cache)
+ return;
+
+ nftnl_rule_list_free(h->rule_cache);
+ h->rule_cache = NULL;
+}
+
+void nft_fini(struct nft_handle *h)
+{
+ flush_rule_cache(h);
+ mnl_socket_close(h->nl);
+ free(mnl_nlmsg_batch_head(h->batch));
+ mnl_nlmsg_batch_stop(h->batch);
+}
+
+static void nft_chain_print_debug(struct nftnl_chain *c, struct nlmsghdr *nlh)
+{
+#ifdef NLDEBUG
+ char tmp[1024];
+
+ nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
+ printf("DEBUG: chain: %s\n", tmp);
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
+#endif
+}
+
+static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
+ const char *table, const char *chain,
+ int policy,
+ const struct xt_counters *counters)
+{
+ struct nftnl_chain *c;
+ struct builtin_table *_t;
+ struct builtin_chain *_c;
+
+ _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);
+
+ _c = nft_chain_builtin_find(_t, chain);
+ if (_c != NULL) {
+ /* This is a built-in chain */
+ c = nft_chain_builtin_alloc(_t, _c, policy);
+ if (c == NULL)
+ return NULL;
+ } else {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (counters) {
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES,
+ counters->bcnt);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS,
+ counters->pcnt);
+ }
+
+ return c;
+}
+
+int nft_chain_set(struct nft_handle *h, const char *table,
+ const char *chain, const char *policy,
+ const struct xt_counters *counters)
+{
+ struct nftnl_chain *c = NULL;
+ int ret;
+
+ nft_fn = nft_chain_set;
+
+ if (strcmp(policy, "DROP") == 0)
+ c = nft_chain_new(h, table, chain, NF_DROP, counters);
+ else if (strcmp(policy, "ACCEPT") == 0)
+ c = nft_chain_new(h, table, chain, NF_ACCEPT, counters);
+
+ if (c == NULL)
+ return 0;
+
+ if (h->batch_support)
+ ret = batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c);
+ else
+ ret = nft_chain_add(h, c, 0);
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m)
+{
+ void *info;
+
+ nftnl_expr_set(e, NFTNL_EXPR_MT_NAME, m->u.user.name, strlen(m->u.user.name));
+ nftnl_expr_set_u32(e, NFTNL_EXPR_MT_REV, m->u.user.revision);
+
+ info = calloc(1, m->u.match_size);
+ if (info == NULL)
+ return -ENOMEM;
+
+ memcpy(info, m->data, m->u.match_size - sizeof(*m));
+ nftnl_expr_set(e, NFTNL_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
+
+ return 0;
+}
+
+int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
+{
+ struct nftnl_expr *expr;
+ int ret;
+
+ expr = nftnl_expr_alloc("match");
+ if (expr == NULL)
+ return -ENOMEM;
+
+ ret = __add_match(expr, m);
+ nftnl_rule_add_expr(r, expr);
+
+ return ret;
+}
+
+static int __add_target(struct nftnl_expr *e, struct xt_entry_target *t)
+{
+ void *info;
+
+ nftnl_expr_set(e, NFTNL_EXPR_TG_NAME, t->u.user.name,
+ strlen(t->u.user.name));
+ nftnl_expr_set_u32(e, NFTNL_EXPR_TG_REV, t->u.user.revision);
+
+ info = calloc(1, t->u.target_size);
+ if (info == NULL)
+ return -ENOMEM;
+
+ memcpy(info, t->data, t->u.target_size - sizeof(*t));
+ nftnl_expr_set(e, NFTNL_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
+
+ return 0;
+}
+
+int add_target(struct nftnl_rule *r, struct xt_entry_target *t)
+{
+ struct nftnl_expr *expr;
+ int ret;
+
+ expr = nftnl_expr_alloc("target");
+ if (expr == NULL)
+ return -ENOMEM;
+
+ ret = __add_target(expr, t);
+ nftnl_rule_add_expr(r, expr);
+
+ return ret;
+}
+
+int add_jumpto(struct nftnl_rule *r, const char *name, int verdict)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("immediate");
+ if (expr == NULL)
+ return -ENOMEM;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, verdict);
+ nftnl_expr_set_str(expr, NFTNL_EXPR_IMM_CHAIN, (char *)name);
+ nftnl_rule_add_expr(r, expr);
+
+ return 0;
+}
+
+int add_verdict(struct nftnl_rule *r, int verdict)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("immediate");
+ if (expr == NULL)
+ return -ENOMEM;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, verdict);
+ nftnl_rule_add_expr(r, expr);
+
+ return 0;
+}
+
+int add_action(struct nftnl_rule *r, struct iptables_command_state *cs,
+ bool goto_set)
+{
+ 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 (goto_set)
+ 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 nftnl_rule *r, struct nlmsghdr *nlh)
+{
+#ifdef NLDEBUG
+ char tmp[1024];
+
+ nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0);
+ printf("DEBUG: rule: %s\n", tmp);
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
+#endif
+}
+
+int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("counter");
+ if (expr == NULL)
+ return -ENOMEM;
+
+ nftnl_expr_set_u64(expr, NFTNL_EXPR_CTR_PACKETS, packets);
+ nftnl_expr_set_u64(expr, NFTNL_EXPR_CTR_BYTES, bytes);
+
+ nftnl_rule_add_expr(r, expr);
+
+ return 0;
+}
+
+enum udata_type {
+ UDATA_TYPE_COMMENT,
+ __UDATA_TYPE_MAX,
+};
+#define UDATA_TYPE_MAX (__UDATA_TYPE_MAX - 1)
+
+int add_comment(struct nftnl_rule *r, const char *comment)
+{
+ struct nftnl_udata_buf *udata;
+
+ udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
+ if (!udata)
+ return -ENOMEM;
+
+ if (!nftnl_udata_put_strz(udata, UDATA_TYPE_COMMENT, comment))
+ return -ENOMEM;
+ nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
+ nftnl_udata_buf_data(udata),
+ nftnl_udata_buf_len(udata));
+
+ nftnl_udata_buf_free(udata);
+
+ return 0;
+}
+
+static int parse_udata_cb(const struct nftnl_udata *attr, void *data)
+{
+ unsigned char *value = nftnl_udata_get(attr);
+ uint8_t type = nftnl_udata_type(attr);
+ uint8_t len = nftnl_udata_len(attr);
+ const struct nftnl_udata **tb = data;
+
+ switch (type) {
+ case UDATA_TYPE_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
+ default:
+ return 0;
+ }
+ tb[type] = attr;
+ return 0;
+}
+
+char *get_comment(const void *data, uint32_t data_len)
+{
+ const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {};
+
+ if (nftnl_udata_parse(data, data_len, parse_udata_cb, tb) < 0)
+ return NULL;
+
+ if (!tb[UDATA_TYPE_COMMENT])
+ return NULL;
+
+ return nftnl_udata_get(tb[UDATA_TYPE_COMMENT]);
+}
+
+void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv)
+{
+ nftnl_rule_set_u32(r, NFTNL_RULE_COMPAT_PROTO, proto);
+ nftnl_rule_set_u32(r, NFTNL_RULE_COMPAT_FLAGS,
+ inv ? NFT_RULE_COMPAT_F_INV : 0);
+}
+
+static struct nftnl_rule *
+nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
+ void *data)
+{
+ struct nftnl_rule *r;
+
+ r = nftnl_rule_alloc();
+ if (r == NULL)
+ return NULL;
+
+ nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, h->family);
+ nftnl_rule_set(r, NFTNL_RULE_TABLE, (char *)table);
+ nftnl_rule_set(r, NFTNL_RULE_CHAIN, (char *)chain);
+
+ if (h->ops->add(r, data) < 0)
+ goto err;
+
+ return r;
+err:
+ nftnl_rule_free(r);
+ return NULL;
+}
+
+int
+nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
+ void *data, uint64_t handle, bool verbose)
+{
+ struct nftnl_rule *r;
+ 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)
+ nft_xt_builtin_init(h, table);
+
+ nft_fn = nft_rule_append;
+
+ r = nft_rule_new(h, chain, table, data);
+ if (r == NULL)
+ return 0;
+
+ if (handle > 0) {
+ nftnl_rule_set(r, NFTNL_RULE_HANDLE, &handle);
+ type = NFT_COMPAT_RULE_REPLACE;
+ } else
+ type = NFT_COMPAT_RULE_APPEND;
+
+ if (batch_rule_add(h, type, r) < 0)
+ nftnl_rule_free(r);
+
+ flush_rule_cache(h);
+ return 1;
+}
+
+void
+nft_rule_print_save(const void *data,
+ struct nftnl_rule *r, enum nft_rule_print type,
+ unsigned int format)
+{
+ const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
+ int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
+ struct nft_family_ops *ops;
+
+ ops = nft_family_ops_lookup(family);
+
+ if (!(format & FMT_NOCOUNTS) && ops->save_counters)
+ ops->save_counters(data);
+
+ /* print chain name */
+ switch(type) {
+ case NFT_RULE_APPEND:
+ printf("-A %s ", chain);
+ break;
+ case NFT_RULE_DEL:
+ printf("-D %s ", chain);
+ break;
+ }
+
+ if (ops->save_firewall)
+ ops->save_firewall(data, format);
+
+}
+
+static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nftnl_chain *c;
+ struct nftnl_chain_list *list = data;
+
+ c = nftnl_chain_alloc();
+ if (c == NULL)
+ goto err;
+
+ if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
+ goto out;
+
+ nftnl_chain_list_add_tail(c, list);
+
+ return MNL_CB_OK;
+out:
+ nftnl_chain_free(c);
+err:
+ return MNL_CB_OK;
+}
+
+static struct nftnl_chain_list *nftnl_chain_list_get(struct nft_handle *h)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nftnl_chain_list *list;
+
+ list = nftnl_chain_list_alloc();
+ if (list == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
+ NLM_F_DUMP, h->seq);
+
+ mnl_talk(h, nlh, nftnl_chain_list_cb, list);
+
+ return list;
+}
+
+struct nftnl_chain_list *nft_chain_dump(struct nft_handle *h)
+{
+ return nftnl_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 nftnl_chain *c, bool basechain)
+{
+ const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ uint64_t pkts = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS);
+ uint64_t bytes = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES);
+
+ /* print chain name */
+ if (basechain) {
+ uint32_t pol = NF_ACCEPT;
+
+ /* no default chain policy? don't crash, display accept */
+ if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
+ pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+
+ printf(":%s %s [%"PRIu64":%"PRIu64"]\n", chain, policy_name[pol],
+ pkts, bytes);
+ } else
+ printf(":%s - [%"PRIu64":%"PRIu64"]\n", chain, pkts, bytes);
+}
+
+int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
+ const char *table)
+{
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *c;
+
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ return 0;
+
+ c = nftnl_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_table =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ bool basechain = false;
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+
+ basechain = nft_chain_builtin(c);
+ nft_chain_print_save(c, basechain);
+next:
+ c = nftnl_chain_list_iter_next(iter);
+ }
+
+ nftnl_chain_list_iter_destroy(iter);
+ nftnl_chain_list_free(list);
+
+ return 1;
+}
+
+static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nftnl_rule *r;
+ struct nftnl_rule_list *list = data;
+
+ r = nftnl_rule_alloc();
+ if (r == NULL)
+ goto err;
+
+ if (nftnl_rule_nlmsg_parse(nlh, r) < 0)
+ goto out;
+
+ nftnl_rule_list_add_tail(r, list);
+
+ return MNL_CB_OK;
+out:
+ nftnl_rule_free(r);
+ nftnl_rule_list_free(list);
+err:
+ return MNL_CB_OK;
+}
+
+static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nftnl_rule_list *list;
+ int ret;
+
+ if (h->rule_cache)
+ return h->rule_cache;
+
+ list = nftnl_rule_list_alloc();
+ if (list == NULL)
+ return 0;
+
+ nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
+ NLM_F_DUMP, h->seq);
+
+ ret = mnl_talk(h, nlh, nftnl_rule_list_cb, list);
+ if (ret < 0) {
+ nftnl_rule_list_free(list);
+ return NULL;
+ }
+
+ h->rule_cache = list;
+ return list;
+}
+
+int nft_rule_save(struct nft_handle *h, const char *table, bool counters)
+{
+ struct nftnl_rule_list *list;
+ struct nftnl_rule_list_iter *iter;
+ struct nftnl_rule *r;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return 0;
+
+ iter = nftnl_rule_list_iter_create(list);
+ if (iter == NULL)
+ return 0;
+
+ r = nftnl_rule_list_iter_next(iter);
+ while (r != NULL) {
+ const char *rule_table =
+ nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
+ struct iptables_command_state cs = {};
+
+ if (strcmp(table, rule_table) != 0)
+ goto next;
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ nft_rule_print_save(&cs, r, NFT_RULE_APPEND,
+ counters ? 0 : FMT_NOCOUNTS);
+
+next:
+ r = nftnl_rule_list_iter_next(iter);
+ }
+
+ nftnl_rule_list_iter_destroy(iter);
+
+ /* 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)
+{
+ struct nftnl_rule *r;
+
+ r = nftnl_rule_alloc();
+ if (r == NULL)
+ return;
+
+ nftnl_rule_set(r, NFTNL_RULE_TABLE, (char *)table);
+ nftnl_rule_set(r, NFTNL_RULE_CHAIN, (char *)chain);
+
+ if (batch_rule_add(h, NFT_COMPAT_RULE_FLUSH, r) < 0)
+ nftnl_rule_free(r);
+}
+
+int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table)
+{
+ int ret;
+ struct nftnl_chain_list *list;
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *c;
+
+ nft_fn = nft_rule_flush;
+
+ list = nftnl_chain_list_get(h);
+ if (list == NULL) {
+ ret = 0;
+ goto err;
+ }
+
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ c = nftnl_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *table_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ const char *chain_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_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);
+
+ if (chain != NULL)
+ break;
+next:
+ c = nftnl_chain_list_iter_next(iter);
+ }
+
+ nftnl_chain_list_iter_destroy(iter);
+ flush_rule_cache(h);
+err:
+ nftnl_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)
+{
+ struct nftnl_chain *c;
+ int ret;
+
+ nft_fn = nft_chain_user_add;
+
+ /* If built-in chains don't exist for this table, create them */
+ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ nft_xt_builtin_init(h, table);
+
+ c = nftnl_chain_alloc();
+ if (c == NULL)
+ return 0;
+
+ nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
+ nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain);
+
+ if (h->batch_support) {
+ ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
+ } else {
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
+ h->family,
+ NLM_F_ACK|NLM_F_EXCL, h->seq);
+ nftnl_chain_nlmsg_build_payload(nlh, c);
+ nftnl_chain_free(c);
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ }
+
+ /* 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 nftnl_chain *c)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, h->family,
+ NLM_F_ACK, h->seq);
+ nftnl_chain_nlmsg_build_payload(nlh, c);
+
+ return mnl_talk(h, nlh, NULL, NULL);
+}
+
+int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table)
+{
+ struct nftnl_chain_list *list;
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *c;
+ int ret = 0;
+ int deleted_ctr = 0;
+
+ list = nftnl_chain_list_get(h);
+ if (list == NULL)
+ goto err;
+
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ c = nftnl_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *table_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ const char *chain_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_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;
+
+ if (h->batch_support)
+ ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
+ else
+ ret = __nft_chain_del(h, c);
+
+ if (ret < 0)
+ break;
+
+ deleted_ctr++;
+
+ if (chain != NULL)
+ break;
+next:
+ c = nftnl_chain_list_iter_next(iter);
+ }
+
+ nftnl_chain_list_iter_destroy(iter);
+err:
+ if (!h->batch_support)
+ nftnl_chain_list_free(list);
+
+ /* chain not found */
+ if (deleted_ctr == 0) {
+ ret = -1;
+ errno = ENOENT;
+ }
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+struct nftnl_chain *
+nft_chain_list_find(struct nftnl_chain_list *list,
+ const char *table, const char *chain)
+{
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *c;
+
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ return NULL;
+
+ c = nftnl_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *table_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ const char *chain_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+
+ if (strcmp(table, table_name) != 0)
+ goto next;
+
+ if (strcmp(chain, chain_name) != 0)
+ goto next;
+
+ nftnl_chain_list_iter_destroy(iter);
+ return c;
+next:
+ c = nftnl_chain_list_iter_next(iter);
+ }
+ nftnl_chain_list_iter_destroy(iter);
+ return NULL;
+}
+
+static struct nftnl_chain *
+nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
+{
+ struct nftnl_chain_list *list;
+
+ list = nftnl_chain_list_get(h);
+ if (list == NULL)
+ return NULL;
+
+ return nft_chain_list_find(list, table, chain);
+}
+
+int nft_chain_user_rename(struct nft_handle *h,const char *chain,
+ const char *table, const char *newname)
+{
+ struct nftnl_chain *c;
+ uint64_t handle;
+ int ret;
+
+ nft_fn = nft_chain_user_add;
+
+ /* If built-in chains don't exist for this table, create them */
+ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ nft_xt_builtin_init(h, table);
+
+ /* Config load changed errno. Ensure genuine info for our callers. */
+ errno = 0;
+
+ /* Find the old chain to be renamed */
+ c = nft_chain_find(h, table, chain);
+ if (c == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ handle = nftnl_chain_get_u64(c, NFTNL_CHAIN_HANDLE);
+
+ /* Now prepare the new name for the chain */
+ c = nftnl_chain_alloc();
+ if (c == NULL)
+ return -1;
+
+ nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
+ nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)newname);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_HANDLE, handle);
+
+ if (h->batch_support) {
+ ret = batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c);
+ } else {
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
+ h->family, NLM_F_ACK, h->seq);
+ nftnl_chain_nlmsg_build_payload(nlh, c);
+ nftnl_chain_free(c);
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ }
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+static int nftnl_table_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nftnl_table *t;
+ struct nftnl_table_list *list = data;
+
+ t = nftnl_table_alloc();
+ if (t == NULL)
+ goto err;
+
+ if (nftnl_table_nlmsg_parse(nlh, t) < 0)
+ goto out;
+
+ nftnl_table_list_add_tail(t, list);
+
+ return MNL_CB_OK;
+out:
+ nftnl_table_free(t);
+err:
+ return MNL_CB_OK;
+}
+
+static struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nftnl_table_list *list;
+
+ list = nftnl_table_list_alloc();
+ if (list == NULL)
+ return 0;
+
+ nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
+ NLM_F_DUMP, h->seq);
+
+ mnl_talk(h, nlh, nftnl_table_list_cb, list);
+
+ return list;
+}
+
+bool nft_table_find(struct nft_handle *h, const char *tablename)
+{
+ struct nftnl_table_list *list;
+ struct nftnl_table_list_iter *iter;
+ struct nftnl_table *t;
+ bool ret = false;
+
+ list = nftnl_table_list_get(h);
+ if (list == NULL)
+ goto err;
+
+ iter = nftnl_table_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ t = nftnl_table_list_iter_next(iter);
+ while (t != NULL) {
+ const char *this_tablename =
+ nftnl_table_get(t, NFTNL_TABLE_NAME);
+
+ if (strcmp(tablename, this_tablename) == 0)
+ return true;
+
+ t = nftnl_table_list_iter_next(iter);
+ }
+
+ nftnl_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 nftnl_table_list *list;
+ struct nftnl_table_list_iter *iter;
+ struct nftnl_table *t;
+
+ list = nftnl_table_list_get(h);
+ if (list == NULL) {
+ ret = 0;
+ goto err;
+ }
+
+ iter = nftnl_table_list_iter_create(list);
+ if (iter == NULL)
+ return 0;
+
+ t = nftnl_table_list_iter_next(iter);
+ while (t != NULL) {
+ const char *tablename =
+ nftnl_table_get(t, NFTNL_TABLE_NAME);
+
+ func(h, tablename, counters);
+
+ t = nftnl_table_list_iter_next(iter);
+ }
+
+ nftnl_table_list_free(list);
+
+err:
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+int nft_table_purge_chains(struct nft_handle *h, const char *this_table,
+ struct nftnl_chain_list *chain_list)
+{
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *chain_obj;
+
+ iter = nftnl_chain_list_iter_create(chain_list);
+ if (iter == NULL)
+ return 0;
+
+ chain_obj = nftnl_chain_list_iter_next(iter);
+ while (chain_obj != NULL) {
+ const char *table =
+ nftnl_chain_get_str(chain_obj, NFTNL_CHAIN_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 = nftnl_chain_list_iter_next(iter);
+ }
+ nftnl_chain_list_iter_destroy(iter);
+
+ return 0;
+}
+
+static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule_list *list,
+ struct nftnl_rule *r)
+{
+ int ret;
+
+ nftnl_rule_list_del(r);
+
+ ret = batch_rule_add(h, NFT_COMPAT_RULE_DELETE, r);
+ if (ret < 0) {
+ nftnl_rule_free(r);
+ return -1;
+ }
+ return 1;
+}
+
+static struct nftnl_rule *
+nft_rule_find(struct nft_handle *h, struct nftnl_rule_list *list,
+ const char *chain, const char *table, void *data, int rulenum)
+{
+ struct nftnl_rule *r;
+ struct nftnl_rule_list_iter *iter;
+ int rule_ctr = 0;
+ bool found = false;
+
+ iter = nftnl_rule_list_iter_create(list);
+ if (iter == NULL)
+ return 0;
+
+ r = nftnl_rule_list_iter_next(iter);
+ while (r != NULL) {
+ const char *rule_table =
+ nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
+ const char *rule_chain =
+ nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
+
+ 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) {
+ found = true;
+ break;
+ }
+ } else {
+ found = h->ops->rule_find(h->ops, r, data);
+ if (found)
+ break;
+ }
+ rule_ctr++;
+next:
+ r = nftnl_rule_list_iter_next(iter);
+ }
+
+ nftnl_rule_list_iter_destroy(iter);
+
+ return found ? r : NULL;
+}
+
+int nft_rule_check(struct nft_handle *h, const char *chain,
+ const char *table, void *data, bool verbose)
+{
+ struct nftnl_rule_list *list;
+ int ret;
+
+ nft_fn = nft_rule_check;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return 0;
+
+ ret = nft_rule_find(h, list, chain, table, data, -1) ? 1 : 0;
+ if (ret == 0)
+ errno = ENOENT;
+
+ return ret;
+}
+
+int nft_rule_delete(struct nft_handle *h, const char *chain,
+ const char *table, void *data, bool verbose)
+{
+ int ret = 0;
+ struct nftnl_rule *r;
+ struct nftnl_rule_list *list;
+
+ nft_fn = nft_rule_delete;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return 0;
+
+ r = nft_rule_find(h, list, chain, table, data, -1);
+ if (r != NULL) {
+ ret =__nft_rule_del(h, list, r);
+ if (ret < 0)
+ errno = ENOMEM;
+ } else
+ errno = ENOENT;
+
+ flush_rule_cache(h);
+
+ 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)
+{
+ struct nftnl_rule *r;
+
+ r = nft_rule_new(h, chain, table, cs);
+ if (r == NULL)
+ return 0;
+
+ if (handle > 0)
+ nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle);
+
+ if (batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r) < 0) {
+ nftnl_rule_free(r);
+ return 0;
+ }
+
+ flush_rule_cache(h);
+ return 1;
+}
+
+int nft_rule_insert(struct nft_handle *h, const char *chain,
+ const char *table, void *data, int rulenum, bool verbose)
+{
+ struct nftnl_rule_list *list;
+ struct nftnl_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_xt_builtin_init(h, table);
+
+ nft_fn = nft_rule_insert;
+
+ if (rulenum > 0) {
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ goto err;
+
+ r = nft_rule_find(h, list, chain, table, data, rulenum);
+ if (r == NULL) {
+ /* special case: iptables allows to insert into
+ * rule_count + 1 position.
+ */
+ r = nft_rule_find(h, list, chain, table, data,
+ rulenum - 1);
+ if (r != NULL) {
+ flush_rule_cache(h);
+ return nft_rule_append(h, chain, table, data,
+ 0, verbose);
+ }
+
+ errno = ENOENT;
+ goto err;
+ }
+
+ handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE);
+ DEBUGP("adding after rule handle %"PRIu64"\n", handle);
+
+ flush_rule_cache(h);
+ }
+
+ return nft_rule_add(h, chain, table, data, handle, verbose);
+err:
+ flush_rule_cache(h);
+ return 0;
+}
+
+int nft_rule_delete_num(struct nft_handle *h, const char *chain,
+ const char *table, int rulenum, bool verbose)
+{
+ int ret = 0;
+ struct nftnl_rule *r;
+ struct nftnl_rule_list *list;
+
+ nft_fn = nft_rule_delete_num;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return 0;
+
+ r = nft_rule_find(h, list, chain, table, NULL, rulenum);
+ if (r != NULL) {
+ ret = 1;
+
+ DEBUGP("deleting rule by number %d\n", rulenum);
+ ret = __nft_rule_del(h, list, r);
+ if (ret < 0)
+ errno = ENOMEM;
+ } else
+ errno = ENOENT;
+
+ flush_rule_cache(h);
+
+ return ret;
+}
+
+int nft_rule_replace(struct nft_handle *h, const char *chain,
+ const char *table, void *data, int rulenum, bool verbose)
+{
+ int ret = 0;
+ struct nftnl_rule *r;
+ struct nftnl_rule_list *list;
+
+ nft_fn = nft_rule_replace;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return 0;
+
+ r = nft_rule_find(h, list, chain, table, data, rulenum);
+ if (r != NULL) {
+ DEBUGP("replacing rule with handle=%llu\n",
+ (unsigned long long)
+ nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
+
+ ret = nft_rule_append(h, chain, table, data,
+ nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
+ verbose);
+ } else
+ errno = ENOENT;
+
+ flush_rule_cache(h);
+
+ return ret;
+}
+
+static int
+__nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ int rulenum, unsigned int format,
+ void (*cb)(struct nftnl_rule *r, unsigned int num,
+ unsigned int format))
+{
+ struct nftnl_rule_list *list;
+ struct nftnl_rule_list_iter *iter;
+ struct nftnl_rule *r;
+ int rule_ctr = 0, ret = 0;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return 0;
+
+ iter = nftnl_rule_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ r = nftnl_rule_list_iter_next(iter);
+ while (r != NULL) {
+ const char *rule_table =
+ nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
+ const char *rule_chain =
+ nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
+
+ if (strcmp(table, rule_table) != 0 ||
+ strcmp(chain, rule_chain) != 0)
+ goto next;
+
+ rule_ctr++;
+
+ if (rulenum > 0 && rule_ctr != rulenum) {
+ /* List by rule number case */
+ goto next;
+ }
+
+ cb(r, rule_ctr, format);
+ if (rulenum > 0 && rule_ctr == rulenum) {
+ ret = 1;
+ break;
+ }
+
+next:
+ r = nftnl_rule_list_iter_next(iter);
+ }
+
+ nftnl_rule_list_iter_destroy(iter);
+err:
+ 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)
+{
+ const struct nft_family_ops *ops;
+ struct nftnl_chain_list *list;
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_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) {
+ nft_xt_builtin_init(h, table);
+ /* Force table and chain creation, otherwise first iptables -L
+ * lists no table/chains.
+ */
+ if (!list_empty(&h->obj_list))
+ nft_commit(h);
+ }
+
+ ops = nft_family_ops_lookup(h->family);
+
+ if (chain && rulenum) {
+ __nft_rule_list(h, chain, table,
+ rulenum, format, ops->print_firewall);
+ return 1;
+ }
+
+ list = nft_chain_dump(h);
+
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ if (ops->print_table_header)
+ ops->print_table_header(table);
+
+ c = nftnl_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_table =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ const char *chain_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ uint32_t policy =
+ nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+ uint32_t refs =
+ nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
+ struct xt_counters ctrs = {
+ .pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
+ .bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
+ };
+ bool basechain = false;
+
+ if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
+ basechain = true;
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+ if (chain && strcmp(chain, chain_name) != 0)
+ goto next;
+
+ if (found)
+ printf("\n");
+
+ ops->print_header(format, chain_name, policy_name[policy],
+ &ctrs, basechain, refs);
+
+ __nft_rule_list(h, chain_name, table,
+ rulenum, format, ops->print_firewall);
+
+ /* we printed the chain we wanted, stop processing. */
+ if (chain)
+ break;
+
+ found = true;
+
+next:
+ c = nftnl_chain_list_iter_next(iter);
+ }
+
+ nftnl_chain_list_iter_destroy(iter);
+err:
+ nftnl_chain_list_free(list);
+
+ return 1;
+}
+
+static void
+list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
+{
+ 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
+nftnl_rule_list_chain_save(struct nft_handle *h, const char *chain,
+ const char *table, struct nftnl_chain_list *list,
+ int counters)
+{
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *c;
+
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ return 0;
+
+ c = nftnl_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_table =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ const char *chain_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ uint32_t policy =
+ nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+
+ if (strcmp(table, chain_table) != 0 ||
+ (chain && strcmp(chain, chain_name) != 0))
+ goto next;
+
+ /* this is a base chain */
+ if (nft_chain_builtin(c)) {
+ printf("-P %s %s", chain_name, policy_name[policy]);
+
+ if (counters) {
+ printf(" -c %"PRIu64" %"PRIu64"\n",
+ nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
+ nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES));
+ } else
+ printf("\n");
+ } else {
+ printf("-N %s\n", chain_name);
+ }
+next:
+ c = nftnl_chain_list_iter_next(iter);
+ }
+
+ nftnl_chain_list_iter_destroy(iter);
+
+ return 1;
+}
+
+int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ const char *table, int rulenum, int counters)
+{
+ struct nftnl_chain_list *list;
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *c;
+ int ret = 1;
+
+ list = nft_chain_dump(h);
+
+ /* Dump policies and custom chains first */
+ if (!rulenum)
+ nftnl_rule_list_chain_save(h, chain, table, list, counters);
+
+ /* Now dump out rules in this table */
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ c = nftnl_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_table =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ const char *chain_name =
+ nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+ if (chain && strcmp(chain, chain_name) != 0)
+ goto next;
+
+ ret = __nft_rule_list(h, chain_name, table, rulenum,
+ counters ? 0 : FMT_NOCOUNTS, list_save);
+
+ /* we printed the chain we wanted, stop processing. */
+ if (chain)
+ break;
+next:
+ c = nftnl_chain_list_iter_next(iter);
+ }
+
+ nftnl_chain_list_iter_destroy(iter);
+err:
+ nftnl_chain_list_free(list);
+
+ 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 nftnl_rule_list *list;
+ struct nftnl_rule *r;
+ int ret = 0;
+
+ nft_fn = nft_rule_delete;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return 0;
+
+ r = nft_rule_find(h, 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,
+ nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
+ false);
+
+error:
+ flush_rule_cache(h);
+
+ return ret;
+}
+
+static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nftnl_table *table)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ type, h->family, flags, seq);
+ nftnl_table_nlmsg_build_payload(nlh, table);
+ nftnl_table_free(table);
+}
+
+static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nftnl_chain *chain)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ type, h->family, flags, seq);
+ nftnl_chain_nlmsg_build_payload(nlh, chain);
+ nft_chain_print_debug(chain, nlh);
+ nftnl_chain_free(chain);
+}
+
+static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nftnl_rule *rule)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ type, h->family, flags, seq);
+ nftnl_rule_nlmsg_build_payload(nlh, rule);
+ nft_rule_print_debug(rule, nlh);
+ nftnl_rule_free(rule);
+}
+
+static int nft_action(struct nft_handle *h, int action)
+{
+ struct obj_update *n, *tmp;
+ uint32_t seq = 1;
+ int ret = 0;
+
+ mnl_nftnl_batch_begin(h->batch, seq++);
+
+ list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
+ switch (n->type) {
+ case NFT_COMPAT_TABLE_ADD:
+ nft_compat_table_batch_add(h, NFT_MSG_NEWTABLE,
+ NLM_F_CREATE, seq++,
+ n->table);
+ break;
+ case NFT_COMPAT_CHAIN_ADD:
+ nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
+ NLM_F_CREATE, seq++,
+ n->chain);
+ break;
+ case NFT_COMPAT_CHAIN_USER_ADD:
+ nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
+ NLM_F_EXCL, seq++,
+ n->chain);
+ break;
+ case NFT_COMPAT_CHAIN_USER_DEL:
+ nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN,
+ 0, seq++, n->chain);
+ break;
+ case NFT_COMPAT_CHAIN_UPDATE:
+ nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
+ h->restore ?
+ NLM_F_CREATE : 0,
+ seq++, n->chain);
+ break;
+ case NFT_COMPAT_CHAIN_RENAME:
+ nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, 0,
+ seq++, n->chain);
+ break;
+ case NFT_COMPAT_RULE_APPEND:
+ nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
+ NLM_F_CREATE | NLM_F_APPEND,
+ seq++, n->rule);
+ break;
+ case NFT_COMPAT_RULE_INSERT:
+ nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
+ NLM_F_CREATE, seq++,
+ n->rule);
+ break;
+ case NFT_COMPAT_RULE_REPLACE:
+ nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
+ NLM_F_CREATE | NLM_F_REPLACE,
+ seq++, n->rule);
+ break;
+ case NFT_COMPAT_RULE_DELETE:
+ case NFT_COMPAT_RULE_FLUSH:
+ nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
+ seq++, n->rule);
+ break;
+ }
+
+ h->obj_list_num--;
+ list_del(&n->head);
+ free(n);
+
+ if (!mnl_nlmsg_batch_next(h->batch))
+ h->batch = mnl_nftnl_batch_page_add(h->batch);
+ }
+
+ switch (action) {
+ case NFT_COMPAT_COMMIT:
+ mnl_nftnl_batch_end(h->batch, seq++);
+ break;
+ case NFT_COMPAT_ABORT:
+ break;
+ }
+
+ if (!mnl_nlmsg_batch_is_empty(h->batch))
+ h->batch = mnl_nftnl_batch_page_add(h->batch);
+
+ ret = mnl_nftnl_batch_talk(h);
+
+ mnl_nlmsg_batch_reset(h->batch);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int nft_commit(struct nft_handle *h)
+{
+ return nft_action(h, NFT_COMPAT_COMMIT);
+}
+
+int nft_abort(struct nft_handle *h)
+{
+ return nft_action(h, NFT_COMPAT_ABORT);
+}
+
+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 ||
+ opt == IP6T_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)
+ return 0;
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
+ goto err;
+
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
+ goto err;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1)
+ goto err;
+
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret == -1)
+ 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, EBUSY, "Directory not empty" },
+ { 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_check, ENOENT, "Bad rule (does a matching rule exist in that chain?)" },
+ { nft_rule_replace, ENOENT, "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);
+}
+
+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 nftnl_table_list *table_list = nftnl_table_list_alloc();
+ struct nftnl_chain_list *chain_list = nftnl_chain_list_alloc();
+ struct nftnl_table_list_iter *titer = NULL;
+ struct nftnl_chain_list_iter *citer = NULL;
+ struct nftnl_table *table;
+ struct nftnl_chain *chain;
+ 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,
+ "configuration file `%s' does not exists\n",
+ filename);
+ } else {
+ xtables_config_perror(flags,
+ "Fatal error parsing config file: %s\n",
+ strerror(errno));
+ }
+ goto err;
+ }
+
+ /* Stage 1) create tables */
+ titer = nftnl_table_list_iter_create(table_list);
+ while ((table = nftnl_table_list_iter_next(titer)) != NULL) {
+ table_family = nftnl_table_get_u32(table,
+ NFTNL_TABLE_FAMILY);
+ if (h->family != table_family)
+ continue;
+
+ found = true;
+
+ if (batch_table_add(h, NFT_COMPAT_TABLE_ADD, table) < 0) {
+ if (errno == EEXIST) {
+ xtables_config_perror(flags,
+ "table `%s' already exists, skipping\n",
+ (char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
+ } else {
+ xtables_config_perror(flags,
+ "table `%s' cannot be create, reason `%s'. Exitting\n",
+ (char *)nftnl_table_get(table, NFTNL_TABLE_NAME),
+ strerror(errno));
+ goto err;
+ }
+ continue;
+ }
+ xtables_config_perror(flags, "table `%s' has been created\n",
+ (char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
+ }
+ nftnl_table_list_iter_destroy(titer);
+ nftnl_table_list_free(table_list);
+
+ if (!found)
+ goto err;
+
+ /* Stage 2) create chains */
+ citer = nftnl_chain_list_iter_create(chain_list);
+ while ((chain = nftnl_chain_list_iter_next(citer)) != NULL) {
+ chain_family = nftnl_chain_get_u32(chain,
+ NFTNL_CHAIN_TABLE);
+ if (h->family != chain_family)
+ continue;
+
+ if (batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, chain) < 0) {
+ if (errno == EEXIST) {
+ xtables_config_perror(flags,
+ "chain `%s' already exists in table `%s', skipping\n",
+ (char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
+ (char *)nftnl_chain_get(chain, NFTNL_CHAIN_TABLE));
+ } else {
+ xtables_config_perror(flags,
+ "chain `%s' cannot be create, reason `%s'. Exitting\n",
+ (char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
+ strerror(errno));
+ goto err;
+ }
+ continue;
+ }
+
+ xtables_config_perror(flags,
+ "chain `%s' in table `%s' has been created\n",
+ (char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
+ (char *)nftnl_chain_get(chain, NFTNL_CHAIN_TABLE));
+ }
+ nftnl_chain_list_iter_destroy(citer);
+ nftnl_chain_list_free(chain_list);
+
+ return 0;
+
+err:
+ nftnl_table_list_free(table_list);
+ nftnl_chain_list_free(chain_list);
+
+ if (titer != NULL)
+ nftnl_table_list_iter_destroy(titer);
+ if (citer != NULL)
+ nftnl_chain_list_iter_destroy(citer);
+
+ return -1;
+}
+
+int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
+ const char *table)
+{
+ struct nftnl_chain_list *list;
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *c;
+ int ret = 0;
+
+ list = nftnl_chain_list_get(h);
+ if (list == NULL)
+ goto err;
+
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ c = nftnl_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_name =
+ nftnl_chain_get(c, NFTNL_CHAIN_NAME);
+ const char *chain_table =
+ nftnl_chain_get(c, NFTNL_CHAIN_TABLE);
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+
+ if (chain != NULL && strcmp(chain, chain_name) != 0)
+ goto next;
+
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
+
+ nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
+
+ if (h->batch_support) {
+ ret = batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
+ } else {
+ struct nlmsghdr *nlh;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+
+ nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
+ h->family, NLM_F_ACK,
+ h->seq);
+ nftnl_chain_nlmsg_build_payload(nlh, c);
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ }
+
+ if (chain != NULL)
+ break;
+next:
+ c = nftnl_chain_list_iter_next(iter);
+ }
+
+ if (!h->batch_support)
+ nftnl_chain_list_free(list);
+
+ nftnl_chain_list_iter_destroy(iter);
+
+err:
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag)
+{
+ if (invflags & flag)
+ return NFT_CMP_NEQ;
+
+ return NFT_CMP_EQ;
+}
+
+#define NFT_COMPAT_EXPR_MAX 8
+
+static const char *supported_exprs[NFT_COMPAT_EXPR_MAX] = {
+ "match",
+ "target",
+ "payload",
+ "meta",
+ "cmp",
+ "bitwise",
+ "counter",
+ "immediate"
+};
+
+
+static int nft_is_expr_compatible(const char *name)
+{
+ int i;
+
+ for (i = 0; i < NFT_COMPAT_EXPR_MAX; i++) {
+ if (strcmp(supported_exprs[i], name) == 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int nft_is_rule_compatible(struct nftnl_rule *rule)
+{
+ struct nftnl_expr_iter *iter;
+ struct nftnl_expr *expr;
+ int ret = 0;
+
+ iter = nftnl_expr_iter_create(rule);
+ if (iter == NULL)
+ return -1;
+
+ expr = nftnl_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+
+ if (nft_is_expr_compatible(name) == 0) {
+ expr = nftnl_expr_iter_next(iter);
+ continue;
+ }
+
+ ret = 1;
+ break;
+ }
+
+ nftnl_expr_iter_destroy(iter);
+ return ret;
+}
+
+static int nft_is_chain_compatible(const char *table, const char *chain)
+{
+ const char *cur_table;
+ struct builtin_chain *chains;
+ int i, j;
+
+ for (i = 0; i < TABLES_MAX; i++) {
+ cur_table = xtables_ipv4[i].name;
+ chains = xtables_ipv4[i].chains;
+
+ if (strcmp(table, cur_table) != 0)
+ continue;
+
+ for (j = 0; j < NF_INET_NUMHOOKS && chains[j].name; j++) {
+ if (strcmp(chain, chains[j].name) == 0)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int nft_are_chains_compatible(struct nft_handle *h)
+{
+ struct nftnl_chain_list *list;
+ struct nftnl_chain_list_iter *iter;
+ struct nftnl_chain *chain;
+ int ret = 0;
+
+ list = nftnl_chain_list_get(h);
+ if (list == NULL)
+ return -1;
+
+ iter = nftnl_chain_list_iter_create(list);
+ if (iter == NULL)
+ return -1;
+
+ chain = nftnl_chain_list_iter_next(iter);
+ while (chain != NULL) {
+ if (!nft_chain_builtin(chain))
+ goto next;
+
+ const char *table = nftnl_chain_get(chain, NFTNL_CHAIN_TABLE);
+ const char *name = nftnl_chain_get(chain, NFTNL_CHAIN_NAME);
+
+ if (nft_is_chain_compatible(table, name) == 1) {
+ ret = 1;
+ break;
+ }
+
+next:
+ chain = nftnl_chain_list_iter_next(iter);
+ }
+
+ nftnl_chain_list_iter_destroy(iter);
+ nftnl_chain_list_free(list);
+ return ret;
+}
+
+static int nft_is_table_compatible(const char *name)
+{
+ int i;
+
+ for (i = 0; i < TABLES_MAX; i++) {
+ if (strcmp(xtables_ipv4[i].name, name) == 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int nft_are_tables_compatible(struct nft_handle *h)
+{
+ struct nftnl_table_list *list;
+ struct nftnl_table_list_iter *iter;
+ struct nftnl_table *table;
+ int ret = 0;
+
+ list = nftnl_table_list_get(h);
+ if (list == NULL)
+ return -1;
+
+ iter = nftnl_table_list_iter_create(list);
+ if (iter == NULL)
+ return -1;
+
+ table = nftnl_table_list_iter_next(iter);
+ while (table != NULL) {
+ const char *name = nftnl_table_get(table, NFTNL_TABLE_NAME);
+
+ if (nft_is_table_compatible(name) == 0) {
+ table = nftnl_table_list_iter_next(iter);
+ continue;
+ }
+
+ ret = 1;
+ break;
+ }
+
+ nftnl_table_list_iter_destroy(iter);
+ nftnl_table_list_free(list);
+ return ret;
+}
+
+int nft_is_ruleset_compatible(struct nft_handle *h)
+{
+
+ struct nftnl_rule_list *list;
+ struct nftnl_rule_list_iter *iter;
+ struct nftnl_rule *rule;
+ int ret = 0;
+
+ ret = nft_are_tables_compatible(h);
+ if (ret != 0)
+ return ret;
+
+ ret = nft_are_chains_compatible(h);
+ if (ret != 0)
+ return ret;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return -1;
+
+ iter = nftnl_rule_list_iter_create(list);
+ if (iter == NULL)
+ return -1;
+
+ rule = nftnl_rule_list_iter_next(iter);
+ while (rule != NULL) {
+ ret = nft_is_rule_compatible(rule);
+ if (ret != 0)
+ break;
+
+ rule = nftnl_rule_list_iter_next(iter);
+ }
+
+ nftnl_rule_list_iter_destroy(iter);
+ return ret;
+}
diff --git a/iptables/nft.h b/iptables/nft.h
new file mode 100644
index 00000000..41265930
--- /dev/null
+++ b/iptables/nft.h
@@ -0,0 +1,187 @@
+#ifndef _NFT_H_
+#define _NFT_H_
+
+#include "xshared.h"
+#include "nft-shared.h"
+#include <libiptc/linux_list.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];
+ bool initialized;
+};
+
+struct nft_handle {
+ int family;
+ struct mnl_socket *nl;
+ uint32_t portid;
+ uint32_t seq;
+ struct list_head obj_list;
+ int obj_list_num;
+ struct mnl_nlmsg_batch *batch;
+ struct nft_family_ops *ops;
+ struct builtin_table *tables;
+ struct nftnl_rule_list *rule_cache;
+ bool restore;
+ bool batch_support;
+};
+
+extern struct builtin_table xtables_ipv4[TABLES_MAX];
+extern struct builtin_table xtables_arp[TABLES_MAX];
+extern struct builtin_table xtables_bridge[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);
+
+/*
+ * Operations with tables.
+ */
+struct nftnl_table;
+struct nftnl_chain_list;
+
+int nft_table_add(struct nft_handle *h, struct nftnl_table *t, uint16_t flags);
+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_purge_chains(struct nft_handle *h, const char *table, struct nftnl_chain_list *list);
+
+/*
+ * Operations with chains.
+ */
+struct nftnl_chain;
+
+int nft_chain_add(struct nft_handle *h, struct nftnl_chain *c, uint16_t flags);
+int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters);
+struct nftnl_chain_list *nft_chain_dump(struct nft_handle *h);
+struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list, const char *table, const char *chain);
+int nft_chain_save(struct nft_handle *h, struct nftnl_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);
+int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char *table);
+
+/*
+ * Operations with rule-set.
+ */
+struct nftnl_rule;
+
+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, 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);
+
+/*
+ * Operations used in userspace tools
+ */
+int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes);
+int add_verdict(struct nftnl_rule *r, int verdict);
+int add_match(struct nftnl_rule *r, struct xt_entry_match *m);
+int add_target(struct nftnl_rule *r, struct xt_entry_target *t);
+int add_jumpto(struct nftnl_rule *r, const char *name, int verdict);
+int add_action(struct nftnl_rule *r, struct iptables_command_state *cs, bool goto_set);
+int add_comment(struct nftnl_rule *r, const char *comment);
+char *get_comment(const void *data, uint32_t data_len);
+
+enum nft_rule_print {
+ NFT_RULE_APPEND,
+ NFT_RULE_DEL,
+};
+
+void nft_rule_print_save(const void *data,
+ struct nftnl_rule *r, enum nft_rule_print type,
+ unsigned int format);
+
+uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
+
+/*
+ * global commit and abort
+ */
+int nft_commit(struct nft_handle *h);
+int nft_abort(struct nft_handle *h);
+
+/*
+ * revision compatibility.
+ */
+int nft_compatible_revision(const char *name, uint8_t rev, int opt);
+
+/*
+ * Error reporting.
+ */
+const char *nft_strerror(int err);
+
+/* For xtables.c */
+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);
+/* For xtables-eb.c */
+int do_commandeb(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 nftnl_table_list;
+struct nftnl_chain_list;
+
+extern int xtables_config_parse(const char *filename, struct nftnl_table_list *table_list, struct nftnl_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);
+
+/*
+ * Translation from iptables to nft
+ */
+struct xt_buf;
+
+bool xlate_find_match(const struct iptables_command_state *cs, const char *p_name);
+int xlate_matches(const struct iptables_command_state *cs, struct xt_xlate *xl);
+int xlate_action(const struct iptables_command_state *cs, bool goto_set,
+ struct xt_xlate *xl);
+void xlate_ifname(struct xt_xlate *xl, const char *nftmeta, const char *ifname,
+ bool invert);
+
+/*
+ * 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);
+
+void nft_rule_to_arpt_entry(struct nftnl_rule *r, struct arpt_entry *fw);
+
+int nft_is_ruleset_compatible(struct nft_handle *h);
+
+#endif
diff --git a/iptables/xshared.c b/iptables/xshared.c
index c144b485..3fbe3b1a 100644
--- a/iptables/xshared.c
+++ b/iptables/xshared.c
@@ -1,4 +1,6 @@
+#include <config.h>
#include <getopt.h>
+#include <errno.h>
#include <libgen.h>
#include <netdb.h>
#include <stdbool.h>
@@ -6,15 +8,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/file.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/time.h>
#include <unistd.h>
+#include <fcntl.h>
#include <xtables.h>
+#include <math.h>
#include "xshared.h"
-#define XT_SOCKET_NAME "xtables"
-#define XT_SOCKET_LEN 8
-
/*
* 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
@@ -243,37 +246,97 @@ void xs_init_match(struct xtables_match *match)
match->init(match->m);
}
-int xtables_lock(bool wait)
+int xtables_lock(int wait, struct timeval *wait_interval)
{
- int i = 0, ret, xt_socket;
- struct sockaddr_un xt_addr;
-
- memset(&xt_addr, 0, sizeof(xt_addr));
- xt_addr.sun_family = AF_UNIX;
- strcpy(xt_addr.sun_path+1, XT_SOCKET_NAME);
- xt_socket = socket(AF_UNIX, SOCK_STREAM, 0);
- /* If we can't even create a socket, fall back to prior (lockless) behavior */
- if (xt_socket < 0)
+ struct timeval time_left, wait_time;
+ int fd, i = 0;
+
+ time_left.tv_sec = wait;
+ time_left.tv_usec = 0;
+
+ fd = open(XT_LOCK_NAME, O_CREAT, 0600);
+ if (fd < 0)
return XT_LOCK_UNSUPPORTED;
+ if (wait == -1) {
+ if (flock(fd, LOCK_EX) == 0)
+ return fd;
+
+ fprintf(stderr, "Can't lock %s: %s\n", XT_LOCK_NAME,
+ strerror(errno));
+ return XT_LOCK_BUSY;
+ }
+
while (1) {
- ret = bind(xt_socket, (struct sockaddr*)&xt_addr,
- offsetof(struct sockaddr_un, sun_path)+XT_SOCKET_LEN);
- if (ret == 0)
- return xt_socket;
- else if (wait == false)
+ if (flock(fd, LOCK_EX | LOCK_NB) == 0)
+ return fd;
+ else if (timercmp(&time_left, wait_interval, <))
return XT_LOCK_BUSY;
- if (++i % 2 == 0)
+
+ if (++i % 10 == 0) {
fprintf(stderr, "Another app is currently holding the xtables lock; "
- "waiting for it to exit...\n");
- sleep(1);
+ "still %lds %ldus time ahead to have a chance to grab the lock...\n",
+ time_left.tv_sec, time_left.tv_usec);
+ }
+
+ wait_time = *wait_interval;
+ select(0, NULL, NULL, NULL, &wait_time);
+ timersub(&time_left, wait_interval, &time_left);
}
}
-void xtables_unlock(int lock) {
- if (lock < 0) {
+void xtables_unlock(int lock)
+{
+ if (lock >= 0)
+ close(lock);
+}
+
+int parse_wait_time(int argc, char *argv[])
+{
+ int wait = -1;
+
+ if (optarg) {
+ if (sscanf(optarg, "%i", &wait) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "wait seconds not numeric");
+ } else if (xs_has_arg(argc, argv))
+ if (sscanf(argv[optind++], "%i", &wait) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "wait seconds not numeric");
+
+ return wait;
+}
+
+void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval)
+{
+ const char *arg;
+ unsigned int usec;
+ int ret;
+
+ if (optarg)
+ arg = optarg;
+ else if (xs_has_arg(argc, argv))
+ arg = argv[optind++];
+ else
+ return;
+
+ ret = sscanf(arg, "%u", &usec);
+ if (ret == 1) {
+ if (usec > 999999)
+ xtables_error(PARAMETER_PROBLEM,
+ "too long usec wait %u > 999999 usec",
+ usec);
+
+ wait_interval->tv_sec = 0;
+ wait_interval->tv_usec = usec;
return;
}
+ xtables_error(PARAMETER_PROBLEM, "wait interval not numeric");
+}
- close(lock);
+inline bool xs_has_arg(int argc, char *argv[])
+{
+ return optind < argc &&
+ argv[optind][0] != '-' &&
+ argv[optind][0] != '!';
}
diff --git a/iptables/xshared.h b/iptables/xshared.h
index 9f9e8031..f8dc5278 100644
--- a/iptables/xshared.h
+++ b/iptables/xshared.h
@@ -6,6 +6,7 @@
#include <stdint.h>
#include <netinet/in.h>
#include <net/if.h>
+#include <sys/time.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
@@ -58,10 +59,12 @@ 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;
char **argv;
+ bool restore;
};
typedef int (*mainfunc_t)(int, char **);
@@ -100,14 +103,17 @@ extern void xs_init_match(struct xtables_match *);
* XT_LOCK_NOT_ACQUIRED : We have not yet attempted to acquire the lock.
*/
enum {
- XT_LOCK_BUSY = -1,
- XT_LOCK_UNSUPPORTED = -2,
- XT_LOCK_NOT_ACQUIRED = -3,
+ XT_LOCK_BUSY = -1,
+ XT_LOCK_UNSUPPORTED = -2,
+ XT_LOCK_NOT_ACQUIRED = -3,
};
-extern int xtables_lock(bool wait);
-
+extern int xtables_lock(int wait, struct timeval *tv);
extern void xtables_unlock(int lock);
+int parse_wait_time(int argc, char *argv[]);
+void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval);
+bool xs_has_arg(int argc, char *argv[]);
+
extern const struct xtables_afinfo *afinfo;
#endif /* IPTABLES_XSHARED_H */
diff --git a/iptables/xtables-arp-standalone.c b/iptables/xtables-arp-standalone.c
new file mode 100644
index 00000000..6553d28f
--- /dev/null
+++ b/iptables/xtables-arp-standalone.c
@@ -0,0 +1,77 @@
+/*
+ * 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 <coreteam@netfilter.org>:
+ * Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * Marc Boucher <marc+nf@mbsi.ca>
+ * James Morris <jmorris@intercode.com.au>
+ * Harald Welte <laforge@gnumonks.org>
+ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <xtables.h>
+#include "nft.h"
+#include <linux/netfilter_arp/arp_tables.h>
+
+#include "xtables-multi.h"
+
+extern struct xtables_globals arptables_globals;
+
+int xtables_arp_main(int argc, char *argv[])
+{
+ int ret;
+ char *table = "filter";
+ struct nft_handle h = {
+ .family = NFPROTO_ARP,
+ };
+
+ arptables_globals.program_name = "arptables";
+ ret = xtables_init_all(&arptables_globals, NFPROTO_ARP);
+ if (ret < 0) {
+ fprintf(stderr, "%s/%s Failed to initialize arptables-compat\n",
+ arptables_globals.program_name,
+ arptables_globals.program_version);
+ exit(1);
+ }
+
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensionsa();
+#endif
+
+ ret = do_commandarp(&h, argc, argv, &table);
+ if (ret)
+ ret = nft_commit(&h);
+
+ nft_fini(&h);
+
+ if (!ret)
+ fprintf(stderr, "arptables: %s\n", nft_strerror(errno));
+
+ exit(!ret);
+}
diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
new file mode 100644
index 00000000..6aa000a1
--- /dev/null
+++ b/iptables/xtables-arp.c
@@ -0,0 +1,1480 @@
+/* Code to take an arptables-style command line and do it. */
+
+/*
+ * arptables:
+ * Author: Bart De Schuymer <bdschuym@pandora.be>, 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 <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <iptables.h>
+#include <xtables.h>
+
+#include "xshared.h"
+
+#include "nft.h"
+#include "nft-arp.h"
+#include <linux/netfilter_arp/arp_tables.h>
+
+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, ...) __attribute__((noreturn, format(printf,2,3)));
+struct xtables_globals arptables_globals = {
+ .option_offset = 0,
+ .program_version = IPTABLES_VERSION,
+ .orig_opts = original_opts,
+ .exit_err = xtables_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 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 = "arptables";
+
+/* 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=<command> 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<<j)))
+ continue;
+
+ if (!(options & (1<<i))) {
+ if (commands_v_options[j][i] == '+')
+ xtables_error(PARAMETER_PROBLEM,
+ "You need to supply the `-%c' "
+ "option for this command\n",
+ optflags[i]);
+ } else {
+ if (commands_v_options[j][i] != 'x')
+ legal = 1;
+ else if (legal == 0)
+ legal = -1;
+ }
+ }
+ if (legal == -1)
+ xtables_error(PARAMETER_PROBLEM,
+ "Illegal option `-%c' with this command\n",
+ optflags[i]);
+ }
+}
+
+static char
+opt2char(int option)
+{
+ const char *ptr;
+ for (ptr = optflags; option > 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 *optidx, int argc)
+{
+ if (option && strcmp(option, "!") == 0) {
+ if (*invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Multiple `!' flags not allowed");
+ *invert = TRUE;
+ if (optidx) {
+ *optidx = *optidx+1;
+ if (argc && *optidx > 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 in_addr *addr;
+ struct addrinfo hints;
+ struct addrinfo *res, *p;
+ int err;
+ unsigned int i;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_RAW;
+
+ *naddr = 0;
+ err = getaddrinfo(name, NULL, &hints, &res);
+ if (err != 0)
+ return NULL;
+ else {
+ for (p = res; p != NULL; p = p->ai_next)
+ (*naddr)++;
+ addr = xtables_calloc(*naddr, sizeof(struct in_addr));
+ for (i = 0, p = res; p != NULL; p = p->ai_next)
+ memcpy(&addr[i++],
+ &((const struct sockaddr_in *)p->ai_addr)->sin_addr,
+ sizeof(struct in_addr));
+ freeaddrinfo(res);
+ 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: weird 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 (!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)+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 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;
+ 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);
+
+ if (target->x6_options != NULL)
+ opts = xtables_options_xfrm(arptables_globals.orig_opts,
+ opts, target->x6_options,
+ &target->option_offset);
+ else
+ opts = xtables_merge_options(arptables_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 arptables_command_state *cs,
+ 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++) {
+ cs->fw.arp.src.s_addr = saddrs[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ cs->fw.arp.tgt.s_addr = daddrs[j].s_addr;
+ if (append) {
+ ret = nft_rule_append(h, chain, table, cs, 0,
+ verbose);
+ } else {
+ ret = nft_rule_insert(h, chain, table, cs,
+ rulenum, verbose);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int
+replace_entry(const char *chain,
+ const char *table,
+ struct arptables_command_state *cs,
+ unsigned int rulenum,
+ const struct in_addr *saddr,
+ const struct in_addr *daddr,
+ bool verbose, struct nft_handle *h)
+{
+ cs->fw.arp.src.s_addr = saddr->s_addr;
+ cs->fw.arp.tgt.s_addr = daddr->s_addr;
+
+ return nft_rule_replace(h, chain, table, cs, rulenum, verbose);
+}
+
+static int
+delete_entry(const char *chain,
+ const char *table,
+ struct arptables_command_state *cs,
+ 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++) {
+ cs->fw.arp.src.s_addr = saddrs[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ cs->fw.arp.tgt.s_addr = daddrs[j].s_addr;
+ ret = nft_rule_delete(h, chain, table, cs, verbose);
+ }
+ }
+
+ return ret;
+}
+
+int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
+{
+ struct arptables_command_state cs;
+ 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 *t;
+
+ memset(&cs, 0, sizeof(cs));
+ cs.jumpto = "";
+
+ 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 (xs_has_arg(argc, argv)) {
+ rulenum = parse_rulenumber(argv[optind++]);
+ command = CMD_DELETE_NUM;
+ }
+ break;
+
+ case 'R':
+ add_command(&command, CMD_REPLACE, CMD_NONE,
+ invert);
+ chain = optarg;
+ if (xs_has_arg(argc, argv))
+ 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 (xs_has_arg(argc, argv))
+ 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 (xs_has_arg(argc, argv))
+ chain = argv[optind++];
+ break;
+
+ case 'F':
+ add_command(&command, CMD_FLUSH, CMD_NONE,
+ invert);
+ if (optarg) chain = optarg;
+ else if (xs_has_arg(argc, argv))
+ chain = argv[optind++];
+ break;
+
+ case 'Z':
+ add_command(&command, CMD_ZERO, CMD_LIST,
+ invert);
+ if (optarg) chain = optarg;
+ else if (xs_has_arg(argc, argv))
+ 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 (xs_has_arg(argc, argv))
+ chain = argv[optind++];
+ break;
+
+ case 'E':
+ add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+ invert);
+ chain = optarg;
+ if (xs_has_arg(argc, argv))
+ 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 (xs_has_arg(argc, argv))
+ 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, &cs.fw.arp.invflags,
+ invert);
+ shostnetworkmask = argv[optind-1];
+ break;
+
+ case 'd':
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_D_IP, &cs.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, &cs.fw.arp.invflags,
+ invert);
+ if (getmac_and_mask(argv[optind - 1],
+ cs.fw.arp.src_devaddr.addr, cs.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, &cs.fw.arp.invflags,
+ invert);
+
+ if (getmac_and_mask(argv[optind - 1],
+ cs.fw.arp.tgt_devaddr.addr, cs.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, &cs.fw.arp.invflags,
+ invert);
+ getlength_and_mask(argv[optind - 1], &cs.fw.arp.arhln,
+ &cs.fw.arp.arhln_mask);
+
+ if (cs.fw.arp.arhln != 6) {
+ xtables_error(PARAMETER_PROBLEM,
+ "Only harware address length of"
+ " 6 is supported currently.");
+ }
+
+ break;
+
+ case 8:/* protocol length */
+ xtables_error(PARAMETER_PROBLEM, "not supported");
+/*
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_P_LENGTH, &cs.fw.arp.invflags,
+ invert);
+
+ getlength_and_mask(argv[optind - 1], &cs.fw.arp.arpln,
+ &cs.fw.arp.arpln_mask);
+ break;
+*/
+
+ case 4:/* opcode */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_OPCODE, &cs.fw.arp.invflags,
+ invert);
+ if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpop,
+ &cs.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");
+ cs.fw.arp.arpop = htons(i+1);
+ }
+ break;
+
+ case 5:/* h-type */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_H_TYPE, &cs.fw.arp.invflags,
+ invert);
+ if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arhrd,
+ &cs.fw.arp.arhrd_mask, 16)) {
+ if (strcasecmp(argv[optind-1], "Ethernet"))
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified hardware type");
+ cs.fw.arp.arhrd = htons(1);
+ }
+ break;
+
+ case 6:/* proto-type */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_P_TYPE, &cs.fw.arp.invflags,
+ invert);
+ if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpro,
+ &cs.fw.arp.arpro_mask, 0)) {
+ if (strcasecmp(argv[optind-1], "ipv4"))
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified protocol type");
+ cs.fw.arp.arpro = htons(0x800);
+ }
+ break;
+
+ case 'j':
+ set_option(&options, OPT_JUMP, &cs.fw.arp.invflags,
+ invert);
+ cs.jumpto = parse_target(optarg);
+ cs.target = command_jump(&cs.fw, cs.jumpto);
+ break;
+
+ case 'i':
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_VIANAMEIN, &cs.fw.arp.invflags,
+ invert);
+ parse_interface(argv[optind-1],
+ cs.fw.arp.iniface,
+ cs.fw.arp.iniface_mask);
+/* cs.fw.nfcache |= NFC_IP_IF_IN; */
+ break;
+
+ case 'o':
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_VIANAMEOUT, &cs.fw.arp.invflags,
+ invert);
+ parse_interface(argv[optind-1],
+ cs.fw.arp.outiface,
+ cs.fw.arp.outiface_mask);
+ /* cs.fw.nfcache |= NFC_IP_IF_OUT; */
+ break;
+
+ case 'v':
+ if (!verbose)
+ set_option(&options, OPT_VERBOSE,
+ &cs.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, &cs.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, &cs.fw.arp.invflags,
+ invert);
+ break;
+
+ case 'M':
+ //modprobe = optarg;
+ break;
+
+ case 'c':
+
+ set_option(&options, OPT_COUNTERS, &cs.fw.arp.invflags,
+ invert);
+ pcnt = optarg;
+ if (xs_has_arg(argc, argv))
+ bcnt = argv[optind++];
+ else
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c requires packet and byte counter",
+ opt2char(OPT_COUNTERS));
+
+ if (sscanf(pcnt, "%llu", &cs.fw.counters.pcnt) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c packet counter not numeric",
+ opt2char(OPT_COUNTERS));
+
+ if (sscanf(bcnt, "%llu", &cs.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:
+ if (cs.target) {
+ xtables_option_tpcall(c, argv,
+ invert, cs.target, &cs.fw);
+ }
+ break;
+ }
+ invert = FALSE;
+ }
+
+ if (cs.target)
+ xtables_option_tfcall(cs.target);
+
+ 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,
+ &(cs.fw.arp.smsk), &nsaddrs);
+
+ if (dhostnetworkmask)
+ parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+ &(cs.fw.arp.tmsk), &ndaddrs);
+
+ if ((nsaddrs > 1 || ndaddrs > 1) &&
+ (cs.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 (!cs.target && strlen(cs.jumpto) != 0) {
+ size_t size;
+
+ cs.target = xtables_find_target(XT_STANDARD_TARGET,
+ XTF_LOAD_MUST_SUCCEED);
+ size = sizeof(struct arpt_entry_target) + cs.target->size;
+ cs.target->t = xtables_calloc(1, size);
+ cs.target->t->u.target_size = size;
+ strcpy(cs.target->t->u.user.name, cs.jumpto);
+ }
+ }
+
+ switch (command) {
+ case CMD_APPEND:
+ ret = append_entry(h, chain, *table, &cs, 0,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE, true);
+ break;
+ case CMD_DELETE:
+ ret = delete_entry(chain, *table, &cs,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE, h);
+ break;
+ case CMD_DELETE_NUM:
+ ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
+ break;
+ case CMD_REPLACE:
+ ret = replace_entry(chain, *table, &cs, rulenum - 1,
+ saddrs, daddrs, options&OPT_VERBOSE, h);
+ break;
+ case CMD_INSERT:
+ ret = append_entry(h, chain, *table, &cs, rulenum - 1,
+ 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 = nft_chain_zero_counters(h, chain, *table);
+ 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-compat-multi.c b/iptables/xtables-compat-multi.c
new file mode 100644
index 00000000..3e24631c
--- /dev/null
+++ b/iptables/xtables-compat-multi.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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},
+ {"iptables-translate", xtables_ip4_xlate_main},
+ {"ip6tables-translate", xtables_ip6_xlate_main},
+ {"iptables-restore-translate", xtables_ip4_xlate_restore_main},
+ {"ip6tables-restore-translate", xtables_ip6_xlate_restore_main},
+ {"arptables", xtables_arp_main},
+ {"arptables-compat", xtables_arp_main},
+ {"ebtables-compat", xtables_eb_main},
+ {NULL},
+};
+
+int main(int argc, char **argv)
+{
+ return subcmd_main(argc, argv, multi_subcommands);
+}
diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y
new file mode 100644
index 00000000..89bfee73
--- /dev/null
+++ b/iptables/xtables-config-parser.y
@@ -0,0 +1,248 @@
+%{
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libiptc/linux_list.h>
+#include <libnftnl/table.h>
+#include <libnftnl/chain.h>
+
+#include <netinet/in.h>
+#include <linux/netfilter.h>
+
+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));
+}
+
+static void stack_free(struct stack_elem *e)
+{
+ free(e);
+}
+
+%}
+
+%union {
+ int val;
+ char *string;
+}
+
+%token T_FAMILY
+%token T_TABLE
+%token T_CHAIN
+%token T_HOOK
+%token T_PRIO
+
+%token <string> T_STRING
+%token <val> T_INTEGER
+
+%%
+
+configfile :
+ | lines
+ ;
+
+lines : line
+ | lines line
+ ;
+
+line : family
+ ;
+
+family : T_FAMILY T_STRING '{' tables '}'
+ {
+ void *data = stack_push(T_FAMILY, strlen($2)+1);
+ stack_put_str(data, $2);
+ }
+ ;
+
+tables : table
+ | 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)+1);
+ stack_put_str(data, $2);
+ }
+ ;
+
+chains : chain
+ | chains chain
+ ;
+
+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)+1);
+ stack_put_str(data, $4);
+ data = stack_push(T_CHAIN, strlen($2)+1);
+ 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;
+}
+
+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 nftnl_table_list *table_list,
+ struct nftnl_chain_list *chain_list)
+{
+ FILE *fp;
+ struct stack_elem *e;
+ struct nftnl_table *table = NULL;
+ struct nftnl_chain *chain = NULL;
+ int prio = 0;
+ int32_t family = 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_FAMILY:
+ family = familytonumber(e->data);
+ if (family == -1)
+ return -1;
+ break;
+ case T_TABLE:
+ table = nftnl_table_alloc();
+ if (table == NULL)
+ return -1;
+
+ nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
+ nftnl_table_set(table, NFTNL_TABLE_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.
+ */
+ nftnl_table_list_add(table, table_list);
+ break;
+ case T_PRIO:
+ memcpy(&prio, e->data, sizeof(int32_t));
+ break;
+ case T_CHAIN:
+ chain = nftnl_chain_alloc();
+ if (chain == NULL)
+ return -1;
+
+ nftnl_chain_set(chain, NFTNL_CHAIN_TABLE,
+ (char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
+ nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY,
+ nftnl_table_get_u32(table, NFTNL_TABLE_FAMILY));
+ nftnl_chain_set_s32(chain, NFTNL_CHAIN_PRIO, prio);
+ nftnl_chain_set(chain, NFTNL_CHAIN_NAME, e->data);
+ /* Intentionally prepending, instead of appending */
+ nftnl_chain_list_add(chain, chain_list);
+ break;
+ case T_HOOK:
+ nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM,
+ hooknametonum(e->data));
+ 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..a895c8bc
--- /dev/null
+++ b/iptables/xtables-config-syntax.l
@@ -0,0 +1,54 @@
+%{
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <http://www.sophos.com>
+ */
+
+#include <string.h>
+#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\.\-\_]*
+
+%%
+"family" { return T_FAMILY; }
+"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} ;
+
+<<EOF>> { yyterminate(); }
+
+. { return yytext[0]; }
+
+%%
+
+int
+yywrap()
+{
+ return 1;
+}
diff --git a/iptables/xtables-eb-standalone.c b/iptables/xtables-eb-standalone.c
new file mode 100644
index 00000000..914d137b
--- /dev/null
+++ b/iptables/xtables-eb-standalone.c
@@ -0,0 +1,74 @@
+/*
+ * 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 <coreteam@netfilter.org>:
+ * Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * Marc Boucher <marc+nf@mbsi.ca>
+ * James Morris <jmorris@intercode.com.au>
+ * Harald Welte <laforge@gnumonks.org>
+ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <xtables.h>
+#include <iptables.h>
+#include "nft.h"
+
+#include "xtables-multi.h"
+
+extern struct xtables_globals ebtables_globals;
+
+int xtables_eb_main(int argc, char *argv[])
+{
+ int ret;
+ char *table = "filter";
+ struct nft_handle h = {
+ .family = NFPROTO_BRIDGE,
+ };
+
+ ebtables_globals.program_name = "ebtables";
+ ret = xtables_init_all(&ebtables_globals, NFPROTO_BRIDGE);
+ if (ret < 0) {
+ fprintf(stderr, "%s/%s Failed to initialize ebtables-compat\n",
+ ebtables_globals.program_name,
+ ebtables_globals.program_version);
+ exit(1);
+ }
+
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensionsb();
+#endif
+ ret = do_commandeb(&h, argc, argv, &table);
+ if (ret)
+ ret = nft_commit(&h);
+
+ if (!ret)
+ fprintf(stderr, "%s\n", nft_strerror(errno));
+
+ exit(!ret);
+}
diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
new file mode 100644
index 00000000..c8b5d4f3
--- /dev/null
+++ b/iptables/xtables-eb.c
@@ -0,0 +1,1379 @@
+/*
+ * ebtables.c, v2.0 July 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ * This code was stongly inspired on the iptables code which is
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * 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 <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <iptables.h>
+#include <xtables.h>
+
+#include <linux/netfilter_bridge.h>
+#include <linux/netfilter/nf_tables.h>
+#include <ebtables/ethernetdb.h>
+#include <libiptc/libxtc.h>
+#include "xshared.h"
+#include "nft.h"
+#include "nft-bridge.h"
+
+/*
+ * From include/ebtables_u.h
+ */
+#define EXEC_STYLE_PRG 0
+#define EXEC_STYLE_DAEMON 1
+
+#define ebt_check_option2(flags, mask) EBT_CHECK_OPTION(flags, mask)
+
+/*
+ * From useful_functions.c
+ */
+
+/* 0: default
+ * 1: the inverse '!' of the option has already been specified */
+int ebt_invert = 0;
+
+unsigned char eb_mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
+unsigned char eb_msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char eb_mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char eb_msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char eb_mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char eb_msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char eb_mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0};
+unsigned char eb_msk_type_bridge_group[ETH_ALEN] = {255,255,255,255,255,255};
+
+int ebt_get_mac_and_mask(const char *from, unsigned char *to,
+ unsigned char *mask)
+{
+ char *p;
+ int i;
+ struct ether_addr *addr = NULL;
+
+ if (strcasecmp(from, "Unicast") == 0) {
+ memcpy(to, eb_mac_type_unicast, ETH_ALEN);
+ memcpy(mask, eb_msk_type_unicast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "Multicast") == 0) {
+ memcpy(to, eb_mac_type_multicast, ETH_ALEN);
+ memcpy(mask, eb_msk_type_multicast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "Broadcast") == 0) {
+ memcpy(to, eb_mac_type_broadcast, ETH_ALEN);
+ memcpy(mask, eb_msk_type_broadcast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "BGA") == 0) {
+ memcpy(to, eb_mac_type_bridge_group, ETH_ALEN);
+ memcpy(mask, eb_msk_type_bridge_group, 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 ebt_check_inverse2(const char option[], int argc, char **argv)
+{
+ if (!option)
+ return ebt_invert;
+ if (strcmp(option, "!") == 0) {
+ if (ebt_invert == 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "Double use of '!' not allowed");
+ if (optind >= argc)
+ optarg = NULL;
+ else
+ optarg = argv[optind];
+ optind++;
+ ebt_invert = 1;
+ return 1;
+ }
+ return ebt_invert;
+}
+
+/*
+ * Glue code to use libxtables
+ */
+static int parse_rule_number(const char *rule)
+{
+ unsigned int rule_nr;
+
+ if (!xtables_strtoui(rule, NULL, &rule_nr, 1, INT_MAX))
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid rule number `%s'", rule);
+
+ return rule_nr;
+}
+
+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 > EBT_CHAIN_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid target '%s' (%d chars max)",
+ targetname, EBT_CHAIN_MAXNAMELEN);
+
+ for (ptr = targetname; *ptr; ptr++)
+ if (isspace(*ptr))
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid target name `%s'", targetname);
+ return targetname;
+}
+
+static int
+append_entry(struct nft_handle *h,
+ const char *chain,
+ const char *table,
+ struct ebtables_command_state *cs,
+ int rule_nr,
+ bool verbose, bool append)
+{
+ int ret = 1;
+
+ if (append)
+ ret = nft_rule_append(h, chain, table, cs, 0, verbose);
+ else
+ ret = nft_rule_insert(h, chain, table, cs, rule_nr, verbose);
+
+ return ret;
+}
+
+static int
+delete_entry(struct nft_handle *h,
+ const char *chain,
+ const char *table,
+ struct ebtables_command_state *cs,
+ int rule_nr,
+ int rule_nr_end,
+ bool verbose)
+{
+ int ret = 1;
+
+ if (rule_nr == -1)
+ ret = nft_rule_delete(h, chain, table, cs, verbose);
+ else {
+ do {
+ ret = nft_rule_delete_num(h, chain, table,
+ rule_nr, verbose);
+ rule_nr++;
+ } while (rule_nr < rule_nr_end);
+ }
+
+ return ret;
+}
+
+static int get_current_chain(const char *chain)
+{
+ if (strcmp(chain, "PREROUTING") == 0)
+ return NF_BR_PRE_ROUTING;
+ else if (strcmp(chain, "INPUT") == 0)
+ return NF_BR_LOCAL_IN;
+ else if (strcmp(chain, "FORWARD") == 0)
+ return NF_BR_FORWARD;
+ else if (strcmp(chain, "OUTPUT") == 0)
+ return NF_BR_LOCAL_OUT;
+ else if (strcmp(chain, "POSTROUTING") == 0)
+ return NF_BR_POST_ROUTING;
+
+ return -1;
+}
+
+/*
+ * The original ebtables parser
+ */
+
+/* Checks whether a command has already been specified */
+#define OPT_COMMANDS (flags & OPT_COMMAND || flags & OPT_ZERO)
+
+#define OPT_COMMAND 0x01
+#define OPT_TABLE 0x02
+#define OPT_IN 0x04
+#define OPT_OUT 0x08
+#define OPT_JUMP 0x10
+#define OPT_PROTOCOL 0x20
+#define OPT_SOURCE 0x40
+#define OPT_DEST 0x80
+#define OPT_ZERO 0x100
+#define OPT_LOGICALIN 0x200
+#define OPT_LOGICALOUT 0x400
+#define OPT_KERNELDATA 0x800 /* This value is also defined in ebtablesd.c */
+#define OPT_COUNT 0x1000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_INCR 0x2000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_DECR 0x4000 /* This value is also defined in libebtc.c */
+
+/* Default command line options. Do not mess around with the already
+ * assigned numbers unless you know what you are doing */
+static struct option ebt_original_options[] =
+{
+ { "append" , required_argument, 0, 'A' },
+ { "insert" , required_argument, 0, 'I' },
+ { "delete" , required_argument, 0, 'D' },
+ { "list" , optional_argument, 0, 'L' },
+ { "Lc" , no_argument , 0, 4 },
+ { "Ln" , no_argument , 0, 5 },
+ { "Lx" , no_argument , 0, 6 },
+ { "Lmac2" , no_argument , 0, 12 },
+ { "zero" , optional_argument, 0, 'Z' },
+ { "flush" , optional_argument, 0, 'F' },
+ { "policy" , required_argument, 0, 'P' },
+ { "in-interface" , required_argument, 0, 'i' },
+ { "in-if" , required_argument, 0, 'i' },
+ { "logical-in" , required_argument, 0, 2 },
+ { "logical-out" , required_argument, 0, 3 },
+ { "out-interface" , required_argument, 0, 'o' },
+ { "out-if" , required_argument, 0, 'o' },
+ { "version" , no_argument , 0, 'V' },
+ { "help" , no_argument , 0, 'h' },
+ { "jump" , required_argument, 0, 'j' },
+ { "set-counters" , required_argument, 0, 'c' },
+ { "change-counters", required_argument, 0, 'C' },
+ { "proto" , required_argument, 0, 'p' },
+ { "protocol" , required_argument, 0, 'p' },
+ { "db" , required_argument, 0, 'b' },
+ { "source" , required_argument, 0, 's' },
+ { "src" , required_argument, 0, 's' },
+ { "destination" , required_argument, 0, 'd' },
+ { "dst" , required_argument, 0, 'd' },
+ { "table" , required_argument, 0, 't' },
+ { "modprobe" , required_argument, 0, 'M' },
+ { "new-chain" , required_argument, 0, 'N' },
+ { "rename-chain" , required_argument, 0, 'E' },
+ { "delete-chain" , optional_argument, 0, 'X' },
+ { "atomic-init" , no_argument , 0, 7 },
+ { "atomic-commit" , no_argument , 0, 8 },
+ { "atomic-file" , required_argument, 0, 9 },
+ { "atomic-save" , no_argument , 0, 10 },
+ { "init-table" , no_argument , 0, 11 },
+ { "concurrent" , no_argument , 0, 13 },
+ { 0 }
+};
+
+static void __attribute__((__noreturn__,format(printf,2,3)))
+ebt_print_error(enum xtables_exittype status, const char *format, ...)
+{
+ va_list l;
+
+ va_start(l, format);
+ vfprintf(stderr, format, l);
+ fprintf(stderr, ".\n");
+ va_end(l);
+ exit(-1);
+}
+
+struct xtables_globals ebtables_globals = {
+ .option_offset = 0,
+ .program_version = IPTABLES_VERSION,
+ .orig_opts = ebt_original_options,
+ .exit_err = ebt_print_error,
+ .compat_rev = nft_compatible_revision,
+};
+
+#define opts ebtables_globals.opts
+#define prog_name ebtables_globals.program_name
+#define prog_vers ebtables_globals.program_version
+
+/*
+ * From libebtc.c
+ */
+
+/* Prints all registered extensions */
+static void ebt_list_extensions(const struct xtables_target *t,
+ const struct xtables_rule_match *m)
+{
+ printf("%s v%s\n", prog_name, prog_vers);
+ printf("Loaded userspace extensions:\n");
+ /*printf("\nLoaded tables:\n");
+ while (tbl) {
+ printf("%s\n", tbl->name);
+ tbl = tbl->next;
+ }*/
+ printf("\nLoaded targets:\n");
+ for (t = xtables_targets; t; t = t->next) {
+ printf("%s\n", t->name);
+ }
+ printf("\nLoaded matches:\n");
+ for (; m != NULL; m = m->next)
+ printf("%s\n", m->match->name);
+ /*printf("\nLoaded watchers:\n");
+ while (w) {
+ printf("%s\n", w->name);
+ w = w->next;
+ }*/
+}
+
+#define OPTION_OFFSET 256
+static struct option *merge_options(struct option *oldopts,
+ const struct option *newopts,
+ unsigned int *options_offset)
+{
+ unsigned int num_old, num_new, i;
+ struct option *merge;
+
+ if (!newopts || !oldopts || !options_offset)
+ return oldopts;
+ for (num_old = 0; oldopts[num_old].name; num_old++);
+ for (num_new = 0; newopts[num_new].name; num_new++);
+
+ ebtables_globals.option_offset += OPTION_OFFSET;
+ *options_offset = ebtables_globals.option_offset;
+
+ merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+ if (!merge)
+ return NULL;
+ memcpy(merge, oldopts, num_old * sizeof(struct option));
+ for (i = 0; i < num_new; i++) {
+ merge[num_old + i] = newopts[i];
+ merge[num_old + i].val += *options_offset;
+ }
+ memset(merge + num_old + num_new, 0, sizeof(struct option));
+ /* Only free dynamically allocated stuff */
+ if (oldopts != ebt_original_options)
+ free(oldopts);
+
+ return merge;
+}
+
+/*
+ * More glue code.
+ */
+static struct xtables_target *command_jump(struct ebtables_command_state *cs,
+ 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;
+ 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);
+
+ opts = merge_options(opts, target->extra_opts, &target->option_offset);
+ if (opts == NULL)
+ xtables_error(OTHER_PROBLEM, "Can't alloc memory");
+
+ return target;
+}
+
+static void print_help(const struct xtables_target *t,
+ const struct xtables_rule_match *m, const char *table)
+{
+ printf("%s %s\n", prog_name, prog_vers);
+ printf(
+"Usage:\n"
+"ebtables -[ADI] chain rule-specification [options]\n"
+"ebtables -P chain target\n"
+"ebtables -[LFZ] [chain]\n"
+"ebtables -[NX] [chain]\n"
+"ebtables -E old-chain-name new-chain-name\n\n"
+"Commands:\n"
+"--append -A chain : append to chain\n"
+"--delete -D chain : delete matching rule from chain\n"
+"--delete -D chain rulenum : delete rule at position rulenum from chain\n"
+"--change-counters -C chain\n"
+" [rulenum] pcnt bcnt : change counters of existing rule\n"
+"--insert -I chain rulenum : insert rule at position rulenum in chain\n"
+"--list -L [chain] : list the rules in a chain or in all chains\n"
+"--flush -F [chain] : delete all rules in chain or in all chains\n"
+"--init-table : replace the kernel table with the initial table\n"
+"--zero -Z [chain] : put counters on zero in chain or in all chains\n"
+"--policy -P chain target : change policy on chain to target\n"
+"--new-chain -N chain : create a user defined chain\n"
+"--rename-chain -E old new : rename a chain\n"
+"--delete-chain -X [chain] : delete a user defined chain\n"
+"--atomic-commit : update the kernel w/t table contained in <FILE>\n"
+"--atomic-init : put the initial kernel table into <FILE>\n"
+"--atomic-save : put the current kernel table into <FILE>\n"
+"--atomic-file file : set <FILE> to file\n\n"
+"Options:\n"
+"--proto -p [!] proto : protocol hexadecimal, by name or LENGTH\n"
+"--src -s [!] address[/mask]: source mac address\n"
+"--dst -d [!] address[/mask]: destination mac address\n"
+"--in-if -i [!] name[+] : network input interface name\n"
+"--out-if -o [!] name[+] : network output interface name\n"
+"--logical-in [!] name[+] : logical bridge input interface name\n"
+"--logical-out [!] name[+] : logical bridge output interface name\n"
+"--set-counters -c chain\n"
+" pcnt bcnt : set the counters of the to be added rule\n"
+"--modprobe -M program : try to insert modules using this program\n"
+"--concurrent : use a file lock to support concurrent scripts\n"
+"--version -V : print package version\n\n"
+"Environment variable:\n"
+/*ATOMIC_ENV_VARIABLE " : if set <FILE> (see above) will equal its value"*/
+"\n\n");
+ for (; m != NULL; m = m->next) {
+ printf("\n");
+ m->match->help();
+ }
+ if (t != NULL) {
+ printf("\n");
+ t->help();
+ }
+
+// if (table->help)
+// table->help(ebt_hooknames);
+}
+
+/* Execute command L */
+static int list_rules(struct nft_handle *h, const char *chain, const char *table,
+ int rule_nr, int verbose, int numeric, int expanded,
+ int linenumbers, int counters)
+{
+ unsigned int format;
+
+ format = FMT_OPTIONS;
+ if (verbose)
+ format |= FMT_VIA;
+
+ if (numeric)
+ format |= FMT_NUMERIC;
+
+ if (!expanded)
+ format |= FMT_KILOMEGAGIGA;
+
+ if (linenumbers)
+ format |= FMT_LINENUMBERS;
+
+ if (!counters)
+ format |= FMT_NOCOUNTS;
+
+ return nft_rule_list(h, chain, table, rule_nr, format);
+}
+
+static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
+{
+ char *colon = strchr(argv, ':'), *buffer;
+
+ if (colon) {
+ *colon = '\0';
+ if (*(colon + 1) == '\0')
+ *rule_nr_end = -1; /* Until the last rule */
+ else {
+ *rule_nr_end = strtol(colon + 1, &buffer, 10);
+ if (*buffer != '\0' || *rule_nr_end == 0)
+ return -1;
+ }
+ }
+ if (colon == argv)
+ *rule_nr = 1; /* Beginning with the first rule */
+ else {
+ *rule_nr = strtol(argv, &buffer, 10);
+ if (*buffer != '\0' || *rule_nr == 0)
+ return -1;
+ }
+ if (!colon)
+ *rule_nr_end = *rule_nr;
+ return 0;
+}
+
+/* Incrementing or decrementing rules in daemon mode is not supported as the
+ * involved code overload is not worth it (too annoying to take the increased
+ * counters in the kernel into account). */
+static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, int exec_style, struct ebtables_command_state *cs)
+{
+ char *buffer;
+ int ret = 0;
+
+ if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')) ||
+ (argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0' && argv[optind + 1][1] > '9')))
+ xtables_error(PARAMETER_PROBLEM,
+ "The command -C needs at least 2 arguments");
+ if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
+ if (optind + 3 != argc)
+ xtables_error(PARAMETER_PROBLEM,
+ "No extra options allowed with -C start_nr[:end_nr] pcnt bcnt");
+ if (parse_rule_range(argv[optind], rule_nr, rule_nr_end))
+ xtables_error(PARAMETER_PROBLEM,
+ "Something is wrong with the rule number specification '%s'", argv[optind]);
+ optind++;
+ }
+
+ if (argv[optind][0] == '+') {
+ if (exec_style == EXEC_STYLE_DAEMON)
+daemon_incr:
+ xtables_error(PARAMETER_PROBLEM,
+ "Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+ ret += 1;
+ cs->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
+ } else if (argv[optind][0] == '-') {
+ if (exec_style == EXEC_STYLE_DAEMON)
+daemon_decr:
+ xtables_error(PARAMETER_PROBLEM,
+ "Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+ ret += 2;
+ cs->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
+ } else
+ cs->counters.pcnt = strtoull(argv[optind], &buffer, 10);
+
+ if (*buffer != '\0')
+ goto invalid;
+ optind++;
+ if (argv[optind][0] == '+') {
+ if (exec_style == EXEC_STYLE_DAEMON)
+ goto daemon_incr;
+ ret += 3;
+ cs->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+ } else if (argv[optind][0] == '-') {
+ if (exec_style == EXEC_STYLE_DAEMON)
+ goto daemon_decr;
+ ret += 6;
+ cs->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+ } else
+ cs->counters.bcnt = strtoull(argv[optind], &buffer, 10);
+
+ if (*buffer != '\0')
+ goto invalid;
+ optind++;
+ return ret;
+invalid:
+ xtables_error(PARAMETER_PROBLEM,"Packet counter '%s' invalid", argv[optind]);
+}
+
+static int parse_iface(char *iface, char *option)
+{
+ char *c;
+
+ if ((c = strchr(iface, '+'))) {
+ if (*(c + 1) != '\0') {
+ xtables_error(PARAMETER_PROBLEM,
+ "Spurious characters after '+' wildcard for '%s'", option);
+ return -1;
+ } else
+ *c = IF_WILDCARD;
+ }
+ return 0;
+}
+
+/* This code is very similar to iptables/xtables.c:command_match() */
+static void ebt_load_match(const char *name)
+{
+ struct xtables_match *m;
+ size_t size;
+
+ m = xtables_find_match(name, XTF_LOAD_MUST_SUCCEED, NULL);
+ if (m == NULL)
+ xtables_error(OTHER_PROBLEM, "Unable to load %s match", name);
+
+ size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
+ m->m = xtables_calloc(1, size);
+ m->m->u.match_size = size;
+ strcpy(m->m->u.user.name, m->name);
+ m->m->u.user.revision = m->revision;
+ xs_init_match(m);
+
+ opts = merge_options(opts, m->extra_opts, &m->option_offset);
+ if (opts == NULL)
+ xtables_error(OTHER_PROBLEM, "Can't alloc memory");
+}
+
+static void ebt_load_watcher(const char *name)
+{
+ struct xtables_target *watcher;
+ size_t size;
+
+ watcher = xtables_find_target(name, XTF_LOAD_MUST_SUCCEED);
+ if (!watcher)
+ xtables_error(OTHER_PROBLEM,
+ "Unable to load %s watcher", name);
+
+ size = XT_ALIGN(sizeof(struct xt_entry_target)) + watcher->size;
+
+ watcher->t = xtables_calloc(1, size);
+ watcher->t->u.target_size = size;
+ strncpy(watcher->t->u.user.name, name,
+ sizeof(watcher->t->u.user.name));
+ watcher->t->u.user.name[sizeof(watcher->t->u.user.name)-1] = '\0';
+ watcher->t->u.user.revision = watcher->revision;
+
+ xs_init_target(watcher);
+
+ opts = merge_options(opts, watcher->extra_opts,
+ &watcher->option_offset);
+ if (opts == NULL)
+ xtables_error(OTHER_PROBLEM, "Can't alloc memory");
+}
+
+static void ebt_load_match_extensions(void)
+{
+ opts = ebt_original_options;
+ ebt_load_match("802_3");
+ ebt_load_match("ip");
+ ebt_load_match("mark_m");
+ ebt_load_match("limit");
+
+ ebt_load_watcher("log");
+ ebt_load_watcher("nflog");
+}
+
+static void ebt_add_match(struct xtables_match *m,
+ struct ebtables_command_state *cs)
+{
+ struct xtables_rule_match *i, **rule_matches = &cs->matches;
+ struct xtables_match *newm;
+ struct ebt_match *newnode;
+
+ /* match already in rule_matches, skip inclusion */
+ for (i = *rule_matches; i; i = i->next) {
+ if (strcmp(m->name, i->match->name) == 0) {
+ i->match->mflags |= m->mflags;
+ return;
+ }
+ }
+
+ newm = xtables_find_match(m->name, XTF_LOAD_MUST_SUCCEED, rule_matches);
+ if (newm == NULL)
+ xtables_error(OTHER_PROBLEM,
+ "Unable to add match %s", m->name);
+
+ newm->mflags = m->mflags;
+
+ /* glue code for watchers */
+ newnode = calloc(1, sizeof(struct ebt_match));
+ if (newnode == NULL)
+ xtables_error(OTHER_PROBLEM, "Unable to alloc memory");
+
+ newnode->ismatch = true;
+ newnode->u.match = newm;
+
+ if (cs->match_list == NULL)
+ cs->match_list = newnode;
+ else
+ cs->match_list->next = newnode;
+}
+
+static void ebt_add_watcher(struct xtables_target *watcher,
+ struct ebtables_command_state *cs)
+{
+ struct ebt_match *i, *newnode;
+
+ for (i = cs->match_list; i; i = i->next) {
+ if (i->ismatch)
+ continue;
+ if (strcmp(i->u.watcher->name, watcher->name) == 0) {
+ i->u.watcher->tflags |= watcher->tflags;
+ return;
+ }
+ }
+
+ newnode = calloc(1, sizeof(struct ebt_match));
+ if (newnode == NULL)
+ xtables_error(OTHER_PROBLEM, "Unable to alloc memory");
+
+ newnode->u.watcher = watcher;
+
+ if (cs->match_list == NULL)
+ cs->match_list = newnode;
+ else
+ cs->match_list->next = newnode;
+}
+
+/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
+int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
+{
+ char *buffer;
+ int c, i;
+ int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */
+ int chcounter = 0; /* Needed for -C */
+ int rule_nr = 0;
+ int rule_nr_end = 0;
+ int ret = 0;
+ unsigned int flags = 0;
+ struct xtables_target *t, *w;
+ struct xtables_match *m;
+ struct ebtables_command_state cs;
+ char command = 'h';
+ const char *chain = NULL;
+ const char *policy = NULL;
+ int exec_style = EXEC_STYLE_PRG;
+ int selected_chain = -1;
+ struct xtables_rule_match *xtrm_i;
+ struct ebt_match *match;
+
+ memset(&cs, 0, sizeof(cs));
+ cs.argv = argv;
+
+ if (nft_init(h, xtables_bridge) < 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");
+
+ /* manually registering ebt matches, given the original ebtables parser
+ * don't use '-m matchname' and the match can't loaded dinamically when
+ * the user calls it.
+ */
+ ebt_load_match_extensions();
+
+ /* clear mflags in case do_commandeb 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;
+ }
+
+ /* prevent getopt to spoil our error reporting */
+ opterr = false;
+
+ /* Getopt saves the day */
+ while ((c = getopt_long(argc, argv,
+ "-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", opts, NULL)) != -1) {
+ cs.c = c;
+ cs.invert = ebt_invert;
+ switch (c) {
+
+ case 'A': /* Add a rule */
+ case 'D': /* Delete a rule */
+ case 'C': /* Change counters */
+ case 'P': /* Define policy */
+ case 'I': /* Insert a rule */
+ case 'N': /* Make a user defined chain */
+ case 'E': /* Rename chain */
+ case 'X': /* Delete chain */
+ /* We allow -N chainname -P policy */
+ /* XXX: Not in ebtables-compat */
+ if (command == 'N' && c == 'P') {
+ command = c;
+ optind--; /* No table specified */
+ goto handle_P;
+ }
+ if (OPT_COMMANDS)
+ xtables_error(PARAMETER_PROBLEM,
+ "Multiple commands are not allowed");
+
+ command = c;
+ chain = optarg;
+ selected_chain = get_current_chain(chain);
+ flags |= OPT_COMMAND;
+ /*if (!(replace->flags & OPT_KERNELDATA))
+ ebt_get_kernel_table(replace, 0);*/
+ /*if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
+ ebt_print_error2("No chain name specified");*/
+ if (c == 'N') {
+ ret = nft_chain_user_add(h, chain, *table);
+ break;
+ } else if (c == 'X') {
+ ret = nft_chain_user_del(h, chain, *table);
+ break;
+ }
+
+ if (c == 'E') {
+ if (optind >= argc)
+ xtables_error(PARAMETER_PROBLEM, "No new chain name specified");
+ else if (optind < argc - 1)
+ xtables_error(PARAMETER_PROBLEM, "No extra options allowed with -E");
+ else if (strlen(argv[optind]) >= NFT_CHAIN_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM, "Chain name length can't exceed %d"" characters", NFT_CHAIN_MAXNAMELEN - 1);
+ else if (strchr(argv[optind], ' ') != NULL)
+ xtables_error(PARAMETER_PROBLEM, "Use of ' ' not allowed in chain names");
+
+ ret = nft_chain_user_rename(h, chain, *table,
+ argv[optind]);
+ if (ret != 0 && errno == ENOENT)
+ xtables_error(PARAMETER_PROBLEM, "Chain '%s' doesn't exists", chain);
+
+ optind++;
+ break;
+ } else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
+ if (optind != argc - 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "No extra options allowed with -D start_nr[:end_nr]");
+ if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the specified rule number(s) '%s'", argv[optind]);
+ optind++;
+ } else if (c == 'C') {
+ if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style, &cs)) == -1)
+ return -1;
+ } else if (c == 'I') {
+ if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
+ rule_nr = 1;
+ else {
+ rule_nr = parse_rule_number(argv[optind]);
+ optind++;
+ }
+ } else if (c == 'P') {
+handle_P:
+ if (optind >= argc)
+ xtables_error(PARAMETER_PROBLEM,
+ "No policy specified");
+ for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ if (!strcmp(argv[optind], nft_ebt_standard_target(i))) {
+ policy = argv[optind];
+ if (-i-1 == EBT_CONTINUE)
+ xtables_error(PARAMETER_PROBLEM,
+ "Wrong policy '%s'",
+ argv[optind]);
+ break;
+ }
+ if (i == NUM_STANDARD_TARGETS)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unknown policy '%s'", argv[optind]);
+ optind++;
+ }
+ break;
+ case 'L': /* List */
+ case 'F': /* Flush */
+ case 'Z': /* Zero counters */
+ if (c == 'Z') {
+ if ((flags & OPT_ZERO) || (flags & OPT_COMMAND && command != 'L'))
+print_zero:
+ xtables_error(PARAMETER_PROBLEM,
+ "Command -Z only allowed together with command -L");
+ flags |= OPT_ZERO;
+ } else {
+ if (flags & OPT_COMMAND)
+ xtables_error(PARAMETER_PROBLEM,
+ "Multiple commands are not allowed");
+ command = c;
+ flags |= OPT_COMMAND;
+ if (flags & OPT_ZERO && c != 'L')
+ goto print_zero;
+ }
+
+#ifdef SILENT_DAEMON
+ if (c== 'L' && exec_style == EXEC_STYLE_DAEMON)
+ xtables_error(PARAMETER_PROBLEM,
+ "-L not supported in daemon mode");
+#endif
+
+ /*if (!(replace->flags & OPT_KERNELDATA))
+ ebt_get_kernel_table(replace, 0);
+ i = -1;
+ if (optind < argc && argv[optind][0] != '-') {
+ if ((i = ebt_get_chainnr(replace, argv[optind])) == -1)
+ ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
+ optind++;
+ }
+ if (i != -1) {
+ if (c == 'Z')
+ zerochain = i;
+ else
+ replace->selected_chain = i;
+ }*/
+ break;
+ case 'V': /* Version */
+ if (OPT_COMMANDS)
+ xtables_error(PARAMETER_PROBLEM,
+ "Multiple commands are not allowed");
+ command = 'V';
+ if (exec_style == EXEC_STYLE_DAEMON)
+ xtables_error(PARAMETER_PROBLEM,
+ "%s %s\n", prog_name, prog_vers);
+ printf("%s %s\n", prog_name, prog_vers);
+ exit(0);
+ case 'h': /* Help */
+#ifdef SILENT_DAEMON
+ if (exec_style == EXEC_STYLE_DAEMON)
+ xtables_error(PARAMETER_PROBLEM,
+ "-h not supported in daemon mode");
+#endif
+ if (OPT_COMMANDS)
+ xtables_error(PARAMETER_PROBLEM,
+ "Multiple commands are not allowed");
+ command = 'h';
+
+ /* All other arguments should be extension names */
+ while (optind < argc) {
+ /*struct ebt_u_match *m;
+ struct ebt_u_watcher *w;*/
+
+ if (!strcasecmp("list_extensions", argv[optind])) {
+ ebt_list_extensions(xtables_targets, cs.matches);
+ exit(0);
+ }
+ /*if ((m = ebt_find_match(argv[optind])))
+ ebt_add_match(new_entry, m);
+ else if ((w = ebt_find_watcher(argv[optind])))
+ ebt_add_watcher(new_entry, w);
+ else {*/
+ if (!(t = xtables_find_target(argv[optind], XTF_TRY_LOAD)))
+ xtables_error(PARAMETER_PROBLEM,"Extension '%s' not found", argv[optind]);
+ if (flags & OPT_JUMP)
+ xtables_error(PARAMETER_PROBLEM,"Sorry, you can only see help for one target extension at a time");
+ flags |= OPT_JUMP;
+ cs.target = t;
+ //}
+ optind++;
+ }
+ break;
+ case 't': /* Table */
+ if (OPT_COMMANDS)
+ xtables_error(PARAMETER_PROBLEM,
+ "Please put the -t option first");
+ ebt_check_option2(&flags, OPT_TABLE);
+ if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "Table name length cannot exceed %d characters",
+ EBT_TABLE_MAXNAMELEN - 1);
+ *table = optarg;
+ break;
+ case 'i': /* Input interface */
+ case 2 : /* Logical input interface */
+ case 'o': /* Output interface */
+ case 3 : /* Logical output interface */
+ case 'j': /* Target */
+ case 'p': /* Net family protocol */
+ case 's': /* Source mac */
+ case 'd': /* Destination mac */
+ case 'c': /* Set counters */
+ if (!OPT_COMMANDS)
+ xtables_error(PARAMETER_PROBLEM,
+ "No command specified");
+ if (command != 'A' && command != 'D' && command != 'I' && command != 'C')
+ xtables_error(PARAMETER_PROBLEM,
+ "Command and option do not match");
+ if (c == 'i') {
+ ebt_check_option2(&flags, OPT_IN);
+ if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
+ xtables_error(PARAMETER_PROBLEM,
+ "Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
+ if (ebt_check_inverse2(optarg, argc, argv))
+ cs.fw.invflags |= EBT_IIN;
+
+ if (strlen(optarg) >= IFNAMSIZ)
+big_iface_length:
+ xtables_error(PARAMETER_PROBLEM,
+ "Interface name length cannot exceed %d characters",
+ IFNAMSIZ - 1);
+ xtables_parse_interface(optarg, cs.fw.in, cs.fw.in_mask);
+ break;
+ } else if (c == 2) {
+ ebt_check_option2(&flags, OPT_LOGICALIN);
+ if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
+ xtables_error(PARAMETER_PROBLEM,
+ "Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
+ if (ebt_check_inverse2(optarg, argc, argv))
+ cs.fw.invflags |= EBT_ILOGICALIN;
+
+ if (strlen(optarg) >= IFNAMSIZ)
+ goto big_iface_length;
+ strcpy(cs.fw.logical_in, optarg);
+ if (parse_iface(cs.fw.logical_in, "--logical-in"))
+ return -1;
+ break;
+ } else if (c == 'o') {
+ ebt_check_option2(&flags, OPT_OUT);
+ if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
+ xtables_error(PARAMETER_PROBLEM,
+ "Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
+ if (ebt_check_inverse2(optarg, argc, argv))
+ cs.fw.invflags |= EBT_IOUT;
+
+ if (strlen(optarg) >= IFNAMSIZ)
+ goto big_iface_length;
+
+ xtables_parse_interface(optarg, cs.fw.out, cs.fw.out_mask);
+ break;
+ } else if (c == 3) {
+ ebt_check_option2(&flags, OPT_LOGICALOUT);
+ if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
+ xtables_error(PARAMETER_PROBLEM,
+ "Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
+ if (ebt_check_inverse2(optarg, argc, argv))
+ cs.fw.invflags |= EBT_ILOGICALOUT;
+
+ if (strlen(optarg) >= IFNAMSIZ)
+ goto big_iface_length;
+ strcpy(cs.fw.logical_out, optarg);
+ if (parse_iface(cs.fw.logical_out, "--logical-out"))
+ return -1;
+ break;
+ } else if (c == 'j') {
+ ebt_check_option2(&flags, OPT_JUMP);
+ cs.jumpto = parse_target(optarg);
+ cs.target = command_jump(&cs, cs.jumpto);
+ break;
+ } else if (c == 's') {
+ ebt_check_option2(&flags, OPT_SOURCE);
+ if (ebt_check_inverse2(optarg, argc, argv))
+ cs.fw.invflags |= EBT_ISOURCE;
+
+ if (ebt_get_mac_and_mask(optarg, cs.fw.sourcemac, cs.fw.sourcemsk))
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified source mac '%s'", optarg);
+ cs.fw.bitmask |= EBT_SOURCEMAC;
+ break;
+ } else if (c == 'd') {
+ ebt_check_option2(&flags, OPT_DEST);
+ if (ebt_check_inverse2(optarg, argc, argv))
+ cs.fw.invflags |= EBT_IDEST;
+
+ if (ebt_get_mac_and_mask(optarg, cs.fw.destmac, cs.fw.destmsk))
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified destination mac '%s'", optarg);
+ cs.fw.bitmask |= EBT_DESTMAC;
+ break;
+ } else if (c == 'c') {
+ ebt_check_option2(&flags, OPT_COUNT);
+ if (ebt_check_inverse2(optarg, argc, argv))
+ xtables_error(PARAMETER_PROBLEM,
+ "Unexpected '!' after -c");
+ if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
+ xtables_error(PARAMETER_PROBLEM,
+ "Option -c needs 2 arguments");
+
+ cs.counters.pcnt = strtoull(optarg, &buffer, 10);
+ if (*buffer != '\0')
+ xtables_error(PARAMETER_PROBLEM,
+ "Packet counter '%s' invalid",
+ optarg);
+ cs.counters.bcnt = strtoull(argv[optind], &buffer, 10);
+ if (*buffer != '\0')
+ xtables_error(PARAMETER_PROBLEM,
+ "Packet counter '%s' invalid",
+ argv[optind]);
+ optind++;
+ break;
+ }
+ ebt_check_option2(&flags, OPT_PROTOCOL);
+ if (ebt_check_inverse2(optarg, argc, argv))
+ cs.fw.invflags |= EBT_IPROTO;
+
+ cs.fw.bitmask &= ~((unsigned int)EBT_NOPROTO);
+ i = strtol(optarg, &buffer, 16);
+ if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the specified protocol");
+ if (*buffer != '\0') {
+ struct ethertypeent *ent;
+
+ if (!strcasecmp(optarg, "LENGTH")) {
+ cs.fw.bitmask |= EBT_802_3;
+ break;
+ }
+ ent = getethertypebyname(optarg);
+ if (!ent)
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);
+ cs.fw.ethproto = ent->e_ethertype;
+ } else
+ cs.fw.ethproto = i;
+
+ if (cs.fw.ethproto < 0x0600)
+ xtables_error(PARAMETER_PROBLEM,
+ "Sorry, protocols have values above or equal to 0x0600");
+ break;
+ case 4 : /* Lc */
+#ifdef SILENT_DAEMON
+ if (exec_style == EXEC_STYLE_DAEMON)
+ xtables_error(PARAMETER_PROBLEM,
+ "--Lc is not supported in daemon mode");
+#endif
+ ebt_check_option2(&flags, LIST_C);
+ if (command != 'L')
+ xtables_error(PARAMETER_PROBLEM,
+ "Use --Lc with -L");
+ flags |= LIST_C;
+ break;
+ case 5 : /* Ln */
+#ifdef SILENT_DAEMON
+ if (exec_style == EXEC_STYLE_DAEMON)
+ xtables_error(PARAMETER_PROBLEM,
+ "--Ln is not supported in daemon mode");
+#endif
+ ebt_check_option2(&flags, LIST_N);
+ if (command != 'L')
+ xtables_error(PARAMETER_PROBLEM,
+ "Use --Ln with -L");
+ if (flags & LIST_X)
+ xtables_error(PARAMETER_PROBLEM,
+ "--Lx is not compatible with --Ln");
+ flags |= LIST_N;
+ break;
+ case 6 : /* Lx */
+#ifdef SILENT_DAEMON
+ if (exec_style == EXEC_STYLE_DAEMON)
+ xtables_error(PARAMETER_PROBLEM,
+ "--Lx is not supported in daemon mode");
+#endif
+ ebt_check_option2(&flags, LIST_X);
+ if (command != 'L')
+ xtables_error(PARAMETER_PROBLEM,
+ "Use --Lx with -L");
+ if (flags & LIST_N)
+ xtables_error(PARAMETER_PROBLEM,
+ "--Lx is not compatible with --Ln");
+ flags |= LIST_X;
+ break;
+ case 12 : /* Lmac2 */
+#ifdef SILENT_DAEMON
+ if (exec_style == EXEC_STYLE_DAEMON)
+ xtables_error(PARAMETER_PROBLEM,
+ "--Lmac2 is not supported in daemon mode");
+#endif
+ ebt_check_option2(&flags, LIST_MAC2);
+ if (command != 'L')
+ xtables_error(PARAMETER_PROBLEM,
+ "Use --Lmac2 with -L");
+ flags |= LIST_MAC2;
+ break;
+ case 8 : /* atomic-commit */
+/* if (exec_style == EXEC_STYLE_DAEMON)
+ ebt_print_error2("--atomic-commit is not supported in daemon mode");
+ replace->command = c;
+ if (OPT_COMMANDS)
+ ebt_print_error2("Multiple commands are not allowed");
+ replace->flags |= OPT_COMMAND;
+ if (!replace->filename)
+ ebt_print_error2("No atomic file specified");*/
+ /* Get the information from the file */
+ /*ebt_get_table(replace, 0);*/
+ /* We don't want the kernel giving us its counters,
+ * they would overwrite the counters extracted from
+ * the file */
+ /*replace->num_counters = 0;*/
+ /* Make sure the table will be written to the kernel */
+ /*free(replace->filename);
+ replace->filename = NULL;
+ break;*/
+ /*case 7 :*/ /* atomic-init */
+ /*case 10:*/ /* atomic-save */
+ /*case 11:*/ /* init-table */
+ /* if (exec_style == EXEC_STYLE_DAEMON) {
+ if (c == 7) {
+ ebt_print_error2("--atomic-init is not supported in daemon mode");
+ } else if (c == 10)
+ ebt_print_error2("--atomic-save is not supported in daemon mode");
+ ebt_print_error2("--init-table is not supported in daemon mode");
+ }
+ replace->command = c;
+ if (OPT_COMMANDS)
+ ebt_print_error2("Multiple commands are not allowed");
+ if (c != 11 && !replace->filename)
+ ebt_print_error2("No atomic file specified");
+ replace->flags |= OPT_COMMAND;
+ {
+ char *tmp = replace->filename;*/
+
+ /* Get the kernel table */
+ /*replace->filename = NULL;
+ ebt_get_kernel_table(replace, c == 10 ? 0 : 1);
+ replace->filename = tmp;
+ }
+ break;
+ case 9 :*/ /* atomic */
+ /*if (exec_style == EXEC_STYLE_DAEMON)
+ ebt_print_error2("--atomic is not supported in daemon mode");
+ if (OPT_COMMANDS)
+ ebt_print_error2("--atomic has to come before the command");*/
+ /* A possible memory leak here, but this is not
+ * executed in daemon mode */
+ /*replace->filename = (char *)malloc(strlen(optarg) + 1);
+ strcpy(replace->filename, optarg);
+ break;
+ case 13 : *//* concurrent */
+ /*signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+ use_lockfd = 1;
+ break;*/
+ case 1 :
+ if (!strcmp(optarg, "!"))
+ ebt_check_inverse2(optarg, argc, argv);
+ else
+ xtables_error(PARAMETER_PROBLEM,
+ "Bad argument : '%s'", optarg);
+ /* ebt_ebt_check_inverse2() did optind++ */
+ optind--;
+ continue;
+ default:
+ /* Is it a target option? */
+ if (cs.target != NULL && cs.target->parse != NULL) {
+ int opt_offset = cs.target->option_offset;
+ if (cs.target->parse(c - opt_offset,
+ argv, ebt_invert,
+ &cs.target->tflags,
+ NULL, &cs.target->t))
+ goto check_extension;
+ }
+
+ /* Is it a match_option? */
+ for (m = xtables_matches; m; m = m->next) {
+ if (m->parse(c - m->option_offset, argv, ebt_invert, &m->mflags, NULL, &m->m)) {
+ ebt_add_match(m, &cs);
+ goto check_extension;
+ }
+ }
+
+ /* Is it a watcher option? */
+ for (w = xtables_targets; w; w = w->next) {
+ if (w->parse(c - w->option_offset, argv,
+ ebt_invert, &w->tflags,
+ NULL, &w->t)) {
+ ebt_add_watcher(w, &cs);
+ goto check_extension;
+ }
+ }
+ /*
+ if (w == NULL && c == '?')
+ ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c);
+ else if (w == NULL) {
+ if (!strcmp(t->name, "standard"))
+ ebt_print_error2("Unknown argument: don't forget the -t option");
+ else
+ ebt_print_error2("Target-specific option does not correspond with specified target");
+ }
+ if (ebt_errormsg[0] != '\0')
+ return -1;
+ if (w->used == 0) {
+ ebt_add_watcher(new_entry, w);
+ w->used = 1;
+ }*/
+check_extension:
+ if (command != 'A' && command != 'I' &&
+ command != 'D' && command != 'C')
+ xtables_error(PARAMETER_PROBLEM,
+ "Extensions only for -A, -I, -D and -C");
+ }
+ ebt_invert = 0;
+ }
+
+ /* Just in case we didn't catch an error */
+ /*if (ebt_errormsg[0] != '\0')
+ return -1;
+
+ if (!(table = ebt_find_table(replace->name)))
+ ebt_print_error2("Bad table name");*/
+
+ if (command == 'h' && !(flags & OPT_ZERO)) {
+ print_help(cs.target, cs.matches, *table);
+ if (exec_style == EXEC_STYLE_PRG)
+ exit(0);
+ }
+
+ /* Do the final checks */
+ if (command == 'A' || command == 'I' ||
+ command == 'D' || command == 'C') {
+ for (xtrm_i = cs.matches; xtrm_i; xtrm_i = xtrm_i->next)
+ xtables_option_mfcall(xtrm_i->match);
+
+ for (match = cs.match_list; match; match = match->next) {
+ if (match->ismatch)
+ continue;
+
+ xtables_option_tfcall(match->u.watcher);
+ }
+
+ if (cs.target != NULL)
+ xtables_option_tfcall(cs.target);
+ }
+ /* So, the extensions can work with the host endian.
+ * The kernel does not have to do this of course */
+ cs.fw.ethproto = htons(cs.fw.ethproto);
+
+ if (command == 'P') {
+ if (selected_chain < 0) {
+ xtables_error(PARAMETER_PROBLEM,
+ "Policy %s not allowed for user defined chains",
+ policy);
+ }
+ if (strcmp(policy, "RETURN") == 0) {
+ xtables_error(PARAMETER_PROBLEM,
+ "Policy RETURN only allowed for user defined chains");
+ }
+ ret = nft_chain_set(h, *table, chain, policy, NULL);
+ if (ret < 0)
+ xtables_error(PARAMETER_PROBLEM, "Wrong policy");
+ } else if (command == 'L') {
+ ret = list_rules(h, chain, *table, rule_nr,
+ flags&OPT_VERBOSE,
+ flags&OPT_NUMERIC,
+ /*flags&OPT_EXPANDED*/0,
+ flags&LIST_N,
+ flags&LIST_C);
+ if (!(flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG)
+ exit(0);
+ }
+ if (flags & OPT_ZERO) {
+ selected_chain = zerochain;
+ ret = nft_chain_zero_counters(h, chain, *table);
+ } else if (command == 'F') {
+ ret = nft_rule_flush(h, chain, *table);
+ } else if (command == 'A') {
+ ret = append_entry(h, chain, *table, &cs, 0,
+ flags&OPT_VERBOSE, true);
+ } else if (command == 'I') {
+ ret = append_entry(h, chain, *table, &cs, rule_nr - 1,
+ flags&OPT_VERBOSE, false);
+ } else if (command == 'D') {
+ ret = delete_entry(h, chain, *table, &cs, rule_nr - 1,
+ rule_nr_end, flags&OPT_VERBOSE);
+ } /*else if (replace->command == 'C') {
+ ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
+ if (ebt_errormsg[0] != '\0')
+ return -1;
+ }*/
+ /* Commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
+ * --init-table fall through */
+
+ /*if (ebt_errormsg[0] != '\0')
+ return -1;
+ if (table->check)
+ table->check(replace);
+
+ if (exec_style == EXEC_STYLE_PRG) {*//* Implies ebt_errormsg[0] == '\0' */
+ /*ebt_deliver_table(replace);
+
+ if (replace->nentries)
+ ebt_deliver_counters(replace);*/
+
+ ebt_cs_clean(&cs);
+ return ret;
+}
diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c
index 8014d5fb..30391e7f 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},
@@ -32,6 +36,15 @@ static const struct subcommand multi_subcommands[] = {
{"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},
+ {"xtables-events", xtables_events_main},
+ {"xtables-arp", xtables_arp_main},
+ {"xtables-ebtables", xtables_eb_main},
+#endif
{NULL},
};
diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h
index 615724b1..7b4195c1 100644
--- a/iptables/xtables-multi.h
+++ b/iptables/xtables-multi.h
@@ -2,5 +2,21 @@
#define _XTABLES_MULTI_H 1
extern int iptables_xml_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_ip4_xlate_main(int, char **);
+extern int xtables_ip6_xlate_main(int, char **);
+extern int xtables_ip4_xlate_restore_main(int, char **);
+extern int xtables_ip6_xlate_restore_main(int, char **);
+extern int xtables_arp_main(int, char **);
+extern int xtables_eb_main(int, char **);
+extern int xtables_config_main(int, char **);
+extern int xtables_events_main(int, char **);
+#endif
#endif /* _XTABLES_MULTI_H */
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
new file mode 100644
index 00000000..a551c8c1
--- /dev/null
+++ b/iptables/xtables-restore.c
@@ -0,0 +1,539 @@
+/* Code to restore the iptables state, from file by iptables-save.
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ */
+
+#include <getopt.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "iptables.h"
+#include "xtables.h"
+#include "libiptc/libiptc.h"
+#include "xtables-multi.h"
+#include "nft.h"
+#include <libnftnl/chain.h>
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+static int counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets. */
+static const struct option options[] = {
+ {.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'},
+ {.name = "ipv4", .has_arg = false, .val = '4'},
+ {.name = "ipv6", .has_arg = false, .val = '6'},
+ {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 [-c] [-v] [-t] [-h] [-n] [-T table] [-M command] [-4] [-6]\n"
+ " [ --counters ]\n"
+ " [ --verbose ]\n"
+ " [ --test ]\n"
+ " [ --help ]\n"
+ " [ --noflush ]\n"
+ " [ --table=<TABLE> ]\n"
+ " [ --modprobe=<command> ]\n"
+ " [ --ipv4 ]\n"
+ " [ --ipv6 ]\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!");
+ }
+ }
+}
+
+static struct nftnl_chain_list *get_chain_list(struct nft_handle *h)
+{
+ struct nftnl_chain_list *chain_list;
+
+ chain_list = nft_chain_dump(h);
+ if (chain_list == NULL)
+ xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n");
+
+ return chain_list;
+}
+
+static void chain_delete(struct nftnl_chain_list *clist, const char *curtable,
+ const char *chain)
+{
+ struct nftnl_chain *chain_obj;
+
+ chain_obj = nft_chain_list_find(clist, curtable, chain);
+ /* This chain has been found, delete from list. Later
+ * on, unvisited chains will be purged out.
+ */
+ if (chain_obj != NULL)
+ nftnl_chain_list_del(chain_obj);
+}
+
+struct nft_xt_restore_cb restore_cb = {
+ .chain_list = get_chain_list,
+ .commit = nft_commit,
+ .abort = nft_abort,
+ .chains_purge = nft_table_purge_chains,
+ .rule_flush = nft_rule_flush,
+ .chain_del = chain_delete,
+ .do_command = do_commandx,
+ .chain_set = nft_chain_set,
+ .chain_user_add = nft_chain_user_add,
+};
+
+static const struct xtc_ops xtc_ops = {
+ .strerror = nft_strerror,
+};
+
+void xtables_restore_parse(struct nft_handle *h,
+ struct nft_xt_restore_parse *p,
+ struct nft_xt_restore_cb *cb,
+ int argc, char *argv[])
+{
+ char buffer[10240];
+ int in_table = 0;
+ char curtable[XT_TABLE_MAXNAMELEN + 1];
+ const struct xtc_ops *ops = &xtc_ops;
+ struct nftnl_chain_list *chain_list = NULL;
+
+ line = 0;
+
+ if (cb->chain_list)
+ chain_list = cb->chain_list(h);
+
+ /* Grab standard input. */
+ while (fgets(buffer, sizeof(buffer), p->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)) {
+ if (!p->testing) {
+ /* Commit per table, although we support
+ * global commit at once, stick by now to
+ * the existing behaviour.
+ */
+ DEBUGP("Calling commit\n");
+ if (cb->commit)
+ ret = cb->commit(h);
+ } else {
+ DEBUGP("Not calling commit, testing\n");
+ if (cb->abort)
+ ret = cb->abort(h);
+ }
+ in_table = 0;
+
+ /* Purge out unused chains in this table */
+ if (!p->testing && cb->chains_purge)
+ cb->chains_purge(h, curtable, chain_list);
+
+ } 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 (p->tablename && (strcmp(p->tablename, table) != 0))
+ continue;
+
+ if (noflush == 0) {
+ DEBUGP("Cleaning all chains of table '%s'\n",
+ table);
+ if (cb->rule_flush)
+ cb->rule_flush(h, NULL, table);
+ }
+
+ ret = 1;
+ in_table = 1;
+
+ if (cb->table_new)
+ cb->table_new(h, table);
+
+ } 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 (cb->chain_del)
+ cb->chain_del(chain_list, curtable, chain);
+
+ 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);
+
+ }
+ if (cb->chain_set &&
+ cb->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);
+ ret = 1;
+
+ } else {
+ if (cb->chain_user_add &&
+ cb->chain_user_add(h, chain, curtable) < 0) {
+ if (errno == EEXIST)
+ continue;
+
+ xtables_error(PARAMETER_PROBLEM,
+ "cannot create chain "
+ "'%s' (%s)\n", chain,
+ strerror(errno));
+ }
+ continue;
+ }
+
+ } 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 = cb->do_command(h, newargc, newargv,
+ &newargv[2], true);
+ if (ret < 0) {
+ if (cb->abort)
+ ret = cb->abort(h);
+ else
+ ret = 0;
+
+ if (ret < 0) {
+ fprintf(stderr, "failed to abort "
+ "commit operation\n");
+ }
+ exit(1);
+ }
+
+ free_argv();
+ fflush(stdout);
+ }
+ if (p->tablename && (strcmp(p->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);
+ }
+}
+
+static int
+xtables_restore_main(int family, const char *progname, int argc, char *argv[])
+{
+ struct nft_handle h = {
+ .family = family,
+ .restore = true,
+ };
+ int c;
+ struct nft_xt_restore_parse p = {};
+
+ line = 0;
+
+ 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,
+ xtables_globals.program_version);
+ exit(1);
+ }
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensions();
+ init_extensions4();
+#endif
+
+ 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,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ while ((c = getopt_long(argc, argv, "bcvthnM:T:46", options, NULL)) != -1) {
+ switch (c) {
+ case 'b':
+ fprintf(stderr, "-b/--binary option is not implemented\n");
+ break;
+ case 'c':
+ counters = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 't':
+ p.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':
+ p.tablename = optarg;
+ break;
+ case '4':
+ h.family = AF_INET;
+ break;
+ case '6':
+ h.family = AF_INET6;
+ xtables_set_nfproto(AF_INET6);
+ break;
+ }
+ }
+
+ if (optind == argc - 1) {
+ p.in = fopen(argv[optind], "re");
+ if (!p.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 {
+ p.in = stdin;
+ }
+
+ xtables_restore_parse(&h, &p, &restore_cb, argc, argv);
+
+ fclose(p.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
new file mode 100644
index 00000000..f30867cf
--- /dev/null
+++ b/iptables/xtables-save.c
@@ -0,0 +1,162 @@
+/* Code to save the xtables state, in human readable-form. */
+/* (C) 1999 by Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ */
+#include <getopt.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netdb.h>
+#include "libiptc/libiptc.h"
+#include "iptables.h"
+#include "xtables-multi.h"
+#include "nft.h"
+
+#include <libnftnl/chain.h>
+
+#ifndef NO_SHARED_LIBS
+#include <dlfcn.h>
+#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'},
+ {.name = "ipv4", .has_arg = false, .val = '4'},
+ {.name = "ipv6", .has_arg = false, .val = '6'},
+ {NULL},
+};
+
+static int
+do_output(struct nft_handle *h, const char *tablename, bool counters)
+{
+ struct nftnl_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
+ */
+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 = family,
+ };
+ int c;
+
+ 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,
+ xtables_globals.program_version);
+ exit(1);
+ }
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensions();
+ init_extensions4();
+#endif
+ 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,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ while ((c = getopt_long(argc, argv, "bcdt:M:46", options, NULL)) != -1) {
+ switch (c) {
+ case 'b':
+ fprintf(stderr, "-b/--binary option is not implemented\n");
+ break;
+ case 'c':
+ show_counters = true;
+ break;
+
+ case 't':
+ /* Select specific table. */
+ tablename = optarg;
+ break;
+ case 'M':
+ xtables_modprobe_program = optarg;
+ break;
+ case 'd':
+ dump = true;
+ break;
+ case '4':
+ h.family = AF_INET;
+ break;
+ case '6':
+ h.family = AF_INET6;
+ xtables_set_nfproto(AF_INET6);
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "Unknown arguments found on commandline\n");
+ exit(1);
+ }
+
+ if (nft_is_ruleset_compatible(&h) == 1) {
+ printf("ERROR: You're using nft features that cannot be mapped to iptables, please keep using nft.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (dump) {
+ do_output(&h, tablename, show_counters);
+ exit(0);
+ }
+
+ 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
new file mode 100644
index 00000000..355a4460
--- /dev/null
+++ b/iptables/xtables-standalone.c
@@ -0,0 +1,104 @@
+/*
+ * 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 <coreteam@netfilter.org>:
+ * Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * Marc Boucher <marc+nf@mbsi.ca>
+ * James Morris <jmorris@intercode.com.au>
+ * Harald Welte <laforge@gnumonks.org>
+ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <iptables.h>
+#include "xtables-multi.h"
+#include "nft.h"
+
+static int
+xtables_main(int family, const char *progname, int argc, char *argv[])
+{
+ int ret;
+ char *table = "filter";
+ struct nft_handle h = {
+ .family = family,
+ };
+
+ 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,
+ xtables_globals.program_version);
+ exit(1);
+ }
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensions();
+ init_extensions4();
+#endif
+
+ 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,
+ strerror(errno));
+ nft_fini(&h);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = do_commandx(&h, argc, argv, &table, false);
+ if (ret)
+ ret = nft_commit(&h);
+
+ nft_fini(&h);
+
+ 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);
+}
+
+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);
+}
diff --git a/iptables/xtables-translate.c b/iptables/xtables-translate.c
new file mode 100644
index 00000000..153bd650
--- /dev/null
+++ b/iptables/xtables-translate.c
@@ -0,0 +1,522 @@
+/*
+ * (C) 2014 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <iptables.h>
+#include <time.h>
+#include "xtables-multi.h"
+#include "nft.h"
+
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <xtables.h>
+#include <libiptc/libxtc.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include "xshared.h"
+#include "nft-shared.h"
+
+void xlate_ifname(struct xt_xlate *xl, const char *nftmeta, const char *ifname,
+ bool invert)
+{
+ char iface[IFNAMSIZ];
+ int ifaclen;
+
+ if (ifname[0] == '\0')
+ return;
+
+ strcpy(iface, ifname);
+ ifaclen = strlen(iface);
+ if (iface[ifaclen - 1] == '+')
+ iface[ifaclen - 1] = '*';
+
+ xt_xlate_add(xl, "%s %s%s ", nftmeta, invert ? "!= " : "", iface);
+}
+
+int xlate_action(const struct iptables_command_state *cs, bool goto_set,
+ struct xt_xlate *xl)
+{
+ int ret = 1, numeric = cs->options & OPT_NUMERIC;
+
+ /* If no target at all, add nothing (default to continue) */
+ if (cs->target != NULL) {
+ /* Standard target? */
+ if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
+ xt_xlate_add(xl, "accept");
+ else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
+ xt_xlate_add(xl, "drop");
+ else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
+ xt_xlate_add(xl, "return");
+ else if (cs->target->xlate) {
+ struct xt_xlate_tg_params params = {
+ .ip = (const void *)&cs->fw,
+ .target = cs->target->t,
+ .numeric = numeric,
+ .escape_quotes = !cs->restore,
+ };
+ ret = cs->target->xlate(xl, &params);
+ }
+ else
+ return 0;
+ } else if (strlen(cs->jumpto) > 0) {
+ /* Not standard, then it's a go / jump to chain */
+ if (goto_set)
+ xt_xlate_add(xl, "goto %s", cs->jumpto);
+ else
+ xt_xlate_add(xl, "jump %s", cs->jumpto);
+ }
+
+ return ret;
+}
+
+int xlate_matches(const struct iptables_command_state *cs, struct xt_xlate *xl)
+{
+ struct xtables_rule_match *matchp;
+ int ret = 1, numeric = cs->options & OPT_NUMERIC;
+
+ for (matchp = cs->matches; matchp; matchp = matchp->next) {
+ struct xt_xlate_mt_params params = {
+ .ip = (const void *)&cs->fw,
+ .match = matchp->match->m,
+ .numeric = numeric,
+ .escape_quotes = !cs->restore,
+ };
+
+ if (!matchp->match->xlate)
+ return 0;
+
+ ret = matchp->match->xlate(xl, &params);
+
+ if (strcmp(matchp->match->name, "comment") != 0)
+ xt_xlate_add(xl, " ");
+
+ if (!ret)
+ break;
+ }
+ return ret;
+}
+
+bool xlate_find_match(const struct iptables_command_state *cs, const char *p_name)
+{
+ struct xtables_rule_match *matchp;
+
+ /* Skip redundant protocol, eg. ip protocol tcp tcp dport */
+ for (matchp = cs->matches; matchp; matchp = matchp->next) {
+ if (strcmp(matchp->match->name, p_name) == 0)
+ return true;
+ }
+ return false;
+}
+
+const char *family2str[] = {
+ [NFPROTO_IPV4] = "ip",
+ [NFPROTO_IPV6] = "ip6",
+};
+
+static int nft_rule_xlate_add(struct nft_handle *h,
+ const struct nft_xt_cmd_parse *p,
+ const struct iptables_command_state *cs,
+ bool append)
+{
+ struct xt_xlate *xl = xt_xlate_alloc(10240);
+ int ret;
+
+ if (append) {
+ xt_xlate_add(xl, "add rule %s %s %s ",
+ family2str[h->family], p->table, p->chain);
+ } else {
+ xt_xlate_add(xl, "insert rule %s %s %s ",
+ family2str[h->family], p->table, p->chain);
+ }
+
+ ret = h->ops->xlate(cs, xl);
+ if (ret)
+ printf("%s\n", xt_xlate_get(xl));
+
+ xt_xlate_free(xl);
+ return ret;
+}
+
+static int xlate(struct nft_handle *h, struct nft_xt_cmd_parse *p,
+ struct iptables_command_state *cs,
+ struct xtables_args *args, bool append,
+ int (*cb)(struct nft_handle *h,
+ const struct nft_xt_cmd_parse *p,
+ const struct iptables_command_state *cs,
+ bool append))
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < args->s.naddrs; i++) {
+ switch (h->family) {
+ case AF_INET:
+ cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr;
+ cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr;
+ for (j = 0; j < args->d.naddrs; j++) {
+ cs->fw.ip.dst.s_addr =
+ args->d.addr.v4[j].s_addr;
+ cs->fw.ip.dmsk.s_addr =
+ args->d.mask.v4[j].s_addr;
+ ret = cb(h, p, cs, append);
+ }
+ break;
+ case AF_INET6:
+ memcpy(&cs->fw6.ipv6.src,
+ &args->s.addr.v6[i], sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.smsk,
+ &args->s.mask.v6[i], sizeof(struct in6_addr));
+ for (j = 0; j < args->d.naddrs; j++) {
+ memcpy(&cs->fw6.ipv6.dst,
+ &args->d.addr.v6[j],
+ sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.dmsk,
+ &args->d.mask.v6[j],
+ sizeof(struct in6_addr));
+ ret = cb(h, p, cs, append);
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void print_ipt_cmd(int argc, char *argv[])
+{
+ int i;
+
+ printf("# ");
+ for (i = 1; i < argc; i++)
+ printf("%s ", argv[i]);
+
+ printf("\n");
+}
+
+static int do_command_xlate(struct nft_handle *h, int argc, char *argv[],
+ char **table, bool restore)
+{
+ int ret = 0;
+ struct nft_xt_cmd_parse p = {
+ .table = *table,
+ .restore = restore,
+ };
+ struct iptables_command_state cs;
+ struct xtables_args args = {
+ .family = h->family,
+ };
+
+ do_parse(h, argc, argv, &p, &cs, &args);
+
+ cs.restore = restore;
+
+ if (!restore)
+ printf("nft ");
+
+ switch (p.command) {
+ case CMD_APPEND:
+ ret = 1;
+ if (!xlate(h, &p, &cs, &args, true, nft_rule_xlate_add)) {
+ print_ipt_cmd(argc, argv);
+ }
+ break;
+ case CMD_DELETE:
+ break;
+ case CMD_DELETE_NUM:
+ break;
+ case CMD_CHECK:
+ break;
+ case CMD_REPLACE:
+ break;
+ case CMD_INSERT:
+ ret = 1;
+ if (!xlate(h, &p, &cs, &args, false, nft_rule_xlate_add)) {
+ print_ipt_cmd(argc, argv);
+ }
+ break;
+ case CMD_FLUSH:
+ if (p.chain) {
+ printf("flush chain %s %s %s\n",
+ family2str[h->family], p.table, p.chain);
+ } else {
+ printf("flush table %s %s\n",
+ family2str[h->family], p.table);
+ }
+ ret = 1;
+ break;
+ case CMD_ZERO:
+ break;
+ case CMD_ZERO_NUM:
+ break;
+ case CMD_LIST:
+ case CMD_LIST|CMD_ZERO:
+ case CMD_LIST|CMD_ZERO_NUM:
+ printf("list table %s %s\n",
+ family2str[h->family], p.table);
+ ret = 1;
+ break;
+ case CMD_LIST_RULES:
+ case CMD_LIST_RULES|CMD_ZERO:
+ case CMD_LIST_RULES|CMD_ZERO_NUM:
+ break;
+ case CMD_NEW_CHAIN:
+ printf("add chain %s %s %s\n",
+ family2str[h->family], p.table, p.chain);
+ ret = 1;
+ break;
+ case CMD_DELETE_CHAIN:
+ printf("delete chain %s %s %s\n",
+ family2str[h->family], p.table, p.chain);
+ ret = 1;
+ break;
+ case CMD_RENAME_CHAIN:
+ break;
+ case CMD_SET_POLICY:
+ break;
+ default:
+ /* We should never reach this... */
+ printf("Unsupported command?\n");
+ exit(1);
+ }
+
+ xtables_rule_matches_free(&cs.matches);
+
+ 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);
+
+ return ret;
+}
+
+static void print_usage(const char *name, const char *version)
+{
+ fprintf(stderr, "%s %s "
+ "(c) 2014 by Pablo Neira Ayuso <pablo@netfilter.org>\n"
+ "Usage: %s [-h] [-f]\n"
+ " [ --help ]\n"
+ " [ --file=<FILE> ]\n", name, version, name);
+ exit(1);
+}
+
+static const struct option options[] = {
+ { .name = "help", .has_arg = false, .val = 'h' },
+ { .name = "file", .has_arg = true, .val = 'f' },
+ { NULL },
+};
+
+static int xlate_chain_user_add(struct nft_handle *h, const char *chain,
+ const char *table)
+{
+ printf("add chain %s %s %s\n", family2str[h->family], table, chain);
+ return 0;
+}
+
+static int commit(struct nft_handle *h)
+{
+ return 1;
+}
+
+static void xlate_table_new(struct nft_handle *h, const char *table)
+{
+ printf("add table %s %s\n", family2str[h->family], table);
+}
+
+static int xlate_chain_set(struct nft_handle *h, const char *table,
+ const char *chain, const char *policy,
+ const struct xt_counters *counters)
+{
+ const char *type = "filter";
+
+ if (strcmp(table, "nat") == 0)
+ type = "nat";
+
+ printf("add chain %s %s %s { type %s ",
+ family2str[h->family], table, chain, type);
+ if (strcmp(chain, "PREROUTING") == 0)
+ printf("hook prerouting priority 0; ");
+ else if (strcmp(chain, "INPUT") == 0)
+ printf("hook input priority 0; ");
+ else if (strcmp(chain, "FORWARD") == 0)
+ printf("hook forward priority 0; ");
+ else if (strcmp(chain, "OUTPUT") == 0)
+ printf("hook output priority 0; ");
+ else if (strcmp(chain, "POSTROUTING") == 0)
+ printf("hook postrouting priority 0; ");
+
+ if (strcmp(policy, "ACCEPT") == 0)
+ printf("policy accept; ");
+ else if (strcmp(policy, "DROP") == 0)
+ printf("policy drop; ");
+
+ printf("}\n");
+ return 1;
+}
+
+static struct nft_xt_restore_cb cb_xlate = {
+ .table_new = xlate_table_new,
+ .chain_set = xlate_chain_set,
+ .chain_user_add = xlate_chain_user_add,
+ .do_command = do_command_xlate,
+ .commit = commit,
+ .abort = commit,
+};
+
+static int xtables_xlate_main(int family, const char *progname, int argc,
+ char *argv[])
+{
+ int ret;
+ char *table = "filter";
+ struct nft_handle h = {
+ .family = family,
+ };
+
+ 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,
+ xtables_globals.program_version);
+ exit(1);
+ }
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensions();
+ init_extensions4();
+#endif
+
+ 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,
+ strerror(errno));
+ nft_fini(&h);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = do_command_xlate(&h, argc, argv, &table, false);
+ if (!ret)
+ fprintf(stderr, "Translation not implemented\n");
+
+ nft_fini(&h);
+ exit(!ret);
+}
+
+static int xtables_restore_xlate_main(int family, const char *progname,
+ int argc, char *argv[])
+{
+ int ret;
+ struct nft_handle h = {
+ .family = family,
+ };
+ const char *file = NULL;
+ struct nft_xt_restore_parse p = {};
+ time_t now = time(NULL);
+ int c;
+
+ 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,
+ xtables_globals.program_version);
+ exit(1);
+ }
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensions();
+ init_extensions4();
+#endif
+
+ 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,
+ strerror(errno));
+ nft_fini(&h);
+ exit(EXIT_FAILURE);
+ }
+
+ opterr = 0;
+ while ((c = getopt_long(argc, argv, "hf:", options, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ print_usage(argv[0], IPTABLES_VERSION);
+ exit(0);
+ case 'f':
+ file = optarg;
+ break;
+ }
+ }
+
+ if (file == NULL) {
+ fprintf(stderr, "ERROR: missing file name\n");
+ print_usage(argv[0], IPTABLES_VERSION);
+ exit(0);
+ }
+
+ p.in = fopen(file, "r");
+ if (p.in == NULL) {
+ fprintf(stderr, "Cannot open file %s\n", file);
+ exit(1);
+ }
+
+ printf("# Translated by %s v%s on %s",
+ argv[0], IPTABLES_VERSION, ctime(&now));
+ xtables_restore_parse(&h, &p, &cb_xlate, argc, argv);
+ printf("# Completed on %s", ctime(&now));
+
+ nft_fini(&h);
+ fclose(p.in);
+ exit(0);
+}
+
+int xtables_ip4_xlate_main(int argc, char *argv[])
+{
+ return xtables_xlate_main(NFPROTO_IPV4, "iptables-translate",
+ argc, argv);
+}
+
+int xtables_ip6_xlate_main(int argc, char *argv[])
+{
+ return xtables_xlate_main(NFPROTO_IPV6, "ip6tables-translate",
+ argc, argv);
+}
+
+int xtables_ip4_xlate_restore_main(int argc, char *argv[])
+{
+ return xtables_restore_xlate_main(NFPROTO_IPV4,
+ "iptables-translate-restore",
+ argc, argv);
+}
+
+int xtables_ip6_xlate_restore_main(int argc, char *argv[])
+{
+ return xtables_restore_xlate_main(NFPROTO_IPV6,
+ "ip6tables-translate-restore",
+ argc, argv);
+}
diff --git a/iptables/xtables.c b/iptables/xtables.c
new file mode 100644
index 00000000..286866f7
--- /dev/null
+++ b/iptables/xtables.c
@@ -0,0 +1,1299 @@
+/* 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 <coreteam@netfilter.org>:
+ * Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * Marc Boucher <marc+nf@mbsi.ca>
+ * James Morris <jmorris@intercode.com.au>
+ * Harald Welte <laforge@gnumonks.org>
+ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * 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 <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <xtables.h>
+#include <fcntl.h>
+#include "xshared.h"
+#include "nft-shared.h"
+#include "nft.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#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 = "wait", .has_arg = 2, .val = 'w'},
+ {.name = "wait-interval", .has_arg = 2, .val = 'W'},
+ {.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 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 = xtables_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
+
+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"
+" --wait -w [seconds] maximum wait to acquire xtables lock before give up\n"
+" --wait-interval -W [usecs] wait time to try to acquire xtables lock\n"
+" default is 1 second\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=<command> 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);
+}
+
+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)
+{
+ 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<<j)))
+ continue;
+
+ if (!(options & (1<<i))) {
+ if (commands_v_options[j][i] == '+')
+ xtables_error(PARAMETER_PROBLEM,
+ "You need to supply the `-%c' "
+ "option for this command\n",
+ optflags[i]);
+ } else {
+ if (commands_v_options[j][i] != 'x')
+ legal = 1;
+ else if (legal == 0)
+ legal = -1;
+ }
+ }
+ if (legal == -1)
+ xtables_error(PARAMETER_PROBLEM,
+ "Illegal option `-%c' with this command\n",
+ optflags[i]);
+ }
+}
+
+static char
+opt2char(int option)
+{
+ const char *ptr;
+ for (ptr = optflags; option > 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,
+ int rulenum, 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 < 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;
+
+ 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,
+ &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));
+ if (append) {
+ ret = nft_rule_append(h, chain, table,
+ cs, 0,
+ verbose);
+ } else {
+ ret = nft_rule_insert(h, chain, table,
+ cs, rulenum,
+ verbose);
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int
+replace_entry(const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ unsigned int rulenum,
+ int family,
+ const struct addr_mask s,
+ const struct addr_mask d,
+ bool verbose, struct nft_handle *h)
+{
+ 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);
+}
+
+static int
+delete_entry(const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ 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 < 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);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int
+check_entry(const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ 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 < 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);
+ }
+ }
+ }
+
+ return ret;
+}
+
+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 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);
+
+ /* iptables does not return error if rule number not found */
+ return 1;
+}
+
+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(xtables_globals.orig_opts, opts,
+ cs->target->x6_options,
+ &cs->target->option_offset);
+ else
+ opts = xtables_merge_options(xtables_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(xtables_globals.orig_opts, opts,
+ m->x6_options, &m->option_offset);
+ else if (m->extra_opts != NULL)
+ 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!");
+}
+
+void do_parse(struct nft_handle *h, int argc, char *argv[],
+ struct nft_xt_cmd_parse *p, struct iptables_command_state *cs,
+ struct xtables_args *args)
+{
+ struct xtables_match *m;
+ struct xtables_rule_match *matchp;
+ bool wait_interval_set = false;
+ struct timeval wait_interval;
+ struct xtables_target *t;
+ int wait = 0;
+
+ 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;
+
+ h->ops = nft_family_ops_lookup(h->family);
+ 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:fbvw::W::nt:m:xc:g:46",
+ opts, NULL)) != -1) {
+ switch (cs->c) {
+ /*
+ * Command selection
+ */
+ case 'A':
+ add_command(&p->command, CMD_APPEND, CMD_NONE,
+ cs->invert);
+ p->chain = optarg;
+ break;
+
+ case 'C':
+ add_command(&p->command, CMD_CHECK, CMD_NONE,
+ cs->invert);
+ p->chain = optarg;
+ break;
+
+ case 'D':
+ add_command(&p->command, CMD_DELETE, CMD_NONE,
+ cs->invert);
+ p->chain = optarg;
+ if (xs_has_arg(argc, argv)) {
+ p->rulenum = parse_rulenumber(argv[optind++]);
+ p->command = CMD_DELETE_NUM;
+ }
+ break;
+
+ case 'R':
+ add_command(&p->command, CMD_REPLACE, CMD_NONE,
+ cs->invert);
+ p->chain = optarg;
+ if (xs_has_arg(argc, argv))
+ p->rulenum = parse_rulenumber(argv[optind++]);
+ else
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c requires a rule number",
+ cmd2char(CMD_REPLACE));
+ break;
+
+ case 'I':
+ add_command(&p->command, CMD_INSERT, CMD_NONE,
+ cs->invert);
+ p->chain = optarg;
+ if (xs_has_arg(argc, argv))
+ p->rulenum = parse_rulenumber(argv[optind++]);
+ else
+ p->rulenum = 1;
+ break;
+
+ case 'L':
+ add_command(&p->command, CMD_LIST,
+ CMD_ZERO | CMD_ZERO_NUM, cs->invert);
+ if (optarg)
+ p->chain = optarg;
+ else if (xs_has_arg(argc, argv))
+ p->chain = argv[optind++];
+ if (xs_has_arg(argc, argv))
+ p->rulenum = parse_rulenumber(argv[optind++]);
+ break;
+
+ case 'S':
+ add_command(&p->command, CMD_LIST_RULES,
+ CMD_ZERO|CMD_ZERO_NUM, cs->invert);
+ if (optarg)
+ p->chain = optarg;
+ else if (xs_has_arg(argc, argv))
+ p->chain = argv[optind++];
+ if (xs_has_arg(argc, argv))
+ p->rulenum = parse_rulenumber(argv[optind++]);
+ break;
+
+ case 'F':
+ add_command(&p->command, CMD_FLUSH, CMD_NONE,
+ cs->invert);
+ if (optarg)
+ p->chain = optarg;
+ else if (xs_has_arg(argc, argv))
+ p->chain = argv[optind++];
+ break;
+
+ case 'Z':
+ add_command(&p->command, CMD_ZERO,
+ CMD_LIST|CMD_LIST_RULES, cs->invert);
+ if (optarg)
+ p->chain = optarg;
+ else if (xs_has_arg(argc, argv))
+ p->chain = argv[optind++];
+ if (xs_has_arg(argc, argv)) {
+ p->rulenum = parse_rulenumber(argv[optind++]);
+ p->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(&p->command, CMD_NEW_CHAIN, CMD_NONE,
+ cs->invert);
+ p->chain = optarg;
+ break;
+
+ case 'X':
+ add_command(&p->command, CMD_DELETE_CHAIN, CMD_NONE,
+ cs->invert);
+ if (optarg)
+ p->chain = optarg;
+ else if (xs_has_arg(argc, argv))
+ p->chain = argv[optind++];
+ break;
+
+ case 'E':
+ add_command(&p->command, CMD_RENAME_CHAIN, CMD_NONE,
+ cs->invert);
+ p->chain = optarg;
+ if (xs_has_arg(argc, argv))
+ p->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(&p->command, CMD_SET_POLICY, CMD_NONE,
+ cs->invert);
+ p->chain = optarg;
+ if (xs_has_arg(argc, argv))
+ p->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,
+ &args->invflags, cs->invert);
+
+ /* Canonicalize into lower case */
+ for (cs->protocol = optarg; *cs->protocol; cs->protocol++)
+ *cs->protocol = tolower(*cs->protocol);
+
+ cs->protocol = optarg;
+ args->proto = xtables_parse_protocol(cs->protocol);
+
+ 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':
+ set_option(&cs->options, OPT_SOURCE,
+ &args->invflags, cs->invert);
+ args->shostnetworkmask = optarg;
+ break;
+
+ case 'd':
+ 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, &args->invflags,
+ cs->invert);
+ args->goto_set = true;
+ 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,
+ &args->invflags, cs->invert);
+ xtables_parse_interface(optarg,
+ args->iniface,
+ args->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,
+ &args->invflags, cs->invert);
+ xtables_parse_interface(optarg,
+ args->outiface,
+ args->outiface_mask);
+ break;
+
+ case 'f':
+ 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, &args->invflags,
+ cs->invert);
+ args->flags |= IPT_F_FRAG;
+ break;
+
+ case 'v':
+ if (!p->verbose)
+ set_option(&cs->options, OPT_VERBOSE,
+ &args->invflags, cs->invert);
+ p->verbose++;
+ break;
+
+ case 'm':
+ command_match(cs);
+ break;
+
+ case 'n':
+ set_option(&cs->options, OPT_NUMERIC, &args->invflags,
+ cs->invert);
+ break;
+
+ case 't':
+ if (cs->invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "unexpected ! flag before --table");
+ p->table = optarg;
+ break;
+
+ case 'x':
+ set_option(&cs->options, OPT_EXPANDED, &args->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 'w':
+ if (p->restore) {
+ xtables_error(PARAMETER_PROBLEM,
+ "You cannot use `-w' from "
+ "iptables-restore");
+ }
+
+ wait = parse_wait_time(argc, argv);
+ break;
+
+ case 'W':
+ if (p->restore) {
+ xtables_error(PARAMETER_PROBLEM,
+ "You cannot use `-W' from "
+ "iptables-restore");
+ }
+
+ parse_wait_interval(argc, argv, &wait_interval);
+ wait_interval_set = true;
+ break;
+
+ case '0':
+ set_option(&cs->options, OPT_LINENUMBERS,
+ &args->invflags, cs->invert);
+ break;
+
+ case 'M':
+ xtables_modprobe_program = optarg;
+ break;
+
+ case 'c':
+ set_option(&cs->options, OPT_COUNTERS, &args->invflags,
+ cs->invert);
+ args->pcnt = optarg;
+ args->bcnt = strchr(args->pcnt + 1, ',');
+ if (args->bcnt)
+ args->bcnt++;
+ if (!args->bcnt && xs_has_arg(argc, argv))
+ args->bcnt = argv[optind++];
+ if (!args->bcnt)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c requires packet and byte counter",
+ opt2char(OPT_COUNTERS));
+
+ if (sscanf(args->pcnt, "%llu", &args->pcnt_cnt) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c packet counter not numeric",
+ opt2char(OPT_COUNTERS));
+
+ 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 (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 */
+ 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, &xtables_globals) == 1)
+ /* cf. ip6tables.c */
+ continue;
+ break;
+ }
+ cs->invert = FALSE;
+ }
+
+ if (strcmp(p->table, "nat") == 0 &&
+ ((p->policy != NULL && strcmp(p->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");
+
+ if (!wait && wait_interval_set)
+ xtables_error(PARAMETER_PROBLEM,
+ "--wait-interval only makes sense with --wait\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 (!p->command)
+ xtables_error(PARAMETER_PROBLEM, "no command specified");
+ if (cs->invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "nothing appropriate following !");
+
+ /* Set only if required, needed by xtables-restore */
+ if (h->family == AF_UNSPEC)
+ h->family = args->family;
+
+ h->ops->post_parse(p->command, cs, args);
+
+ if (p->command == CMD_REPLACE &&
+ (args->s.naddrs != 1 || args->d.naddrs != 1))
+ xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
+ "specify a unique address");
+
+ generic_opt_check(p->command, cs->options);
+
+ if (p->chain != NULL && strlen(p->chain) >= XT_EXTENSION_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name `%s' too long (must be under %u chars)",
+ p->chain, XT_EXTENSION_MAXNAMELEN);
+
+ if (p->command == CMD_APPEND ||
+ p->command == CMD_DELETE ||
+ p->command == CMD_CHECK ||
+ p->command == CMD_INSERT ||
+ p->command == CMD_REPLACE) {
+ if (strcmp(p->chain, "PREROUTING") == 0
+ || strcmp(p->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),
+ p->chain);
+ }
+
+ if (strcmp(p->chain, "POSTROUTING") == 0
+ || strcmp(p->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),
+ p->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.
+ */
+ }
+}
+
+int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
+ bool restore)
+{
+ int ret = 1;
+ struct nft_xt_cmd_parse p = {
+ .table = *table,
+ .restore = restore,
+ };
+ struct iptables_command_state cs;
+ struct xtables_args args = {
+ .family = h->family,
+ };
+
+ do_parse(h, argc, argv, &p, &cs, &args);
+
+ switch (p.command) {
+ case CMD_APPEND:
+ ret = add_entry(p.chain, p.table, &cs, 0, h->family,
+ args.s, args.d,
+ cs.options & OPT_VERBOSE, h, true);
+ break;
+ case CMD_DELETE:
+ ret = delete_entry(p.chain, p.table, &cs, h->family,
+ args.s, args.d,
+ cs.options & OPT_VERBOSE, h);
+ break;
+ case CMD_DELETE_NUM:
+ ret = nft_rule_delete_num(h, p.chain, p.table,
+ p.rulenum - 1, p.verbose);
+ break;
+ case CMD_CHECK:
+ ret = check_entry(p.chain, p.table, &cs, h->family,
+ args.s, args.d,
+ cs.options & OPT_VERBOSE, h);
+ break;
+ case CMD_REPLACE:
+ ret = replace_entry(p.chain, p.table, &cs, p.rulenum - 1,
+ h->family, args.s, args.d,
+ cs.options & OPT_VERBOSE, h);
+ break;
+ case CMD_INSERT:
+ ret = add_entry(p.chain, p.table, &cs, p.rulenum - 1,
+ h->family, args.s, args.d,
+ cs.options&OPT_VERBOSE, h, false);
+ break;
+ case CMD_FLUSH:
+ ret = nft_rule_flush(h, p.chain, p.table);
+ break;
+ case CMD_ZERO:
+ ret = nft_chain_zero_counters(h, p.chain, p.table);
+ break;
+ case CMD_ZERO_NUM:
+ ret = nft_rule_zero_counters(h, p.chain, p.table,
+ p.rulenum - 1);
+ break;
+ case CMD_LIST:
+ case CMD_LIST|CMD_ZERO:
+ case CMD_LIST|CMD_ZERO_NUM:
+ if (nft_is_ruleset_compatible(h) == 1) {
+ printf("ERROR: You're using nft features that cannot be mapped to iptables, please keep using nft.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = list_entries(h, p.chain, p.table, p.rulenum,
+ cs.options & OPT_VERBOSE,
+ cs.options & OPT_NUMERIC,
+ cs.options & OPT_EXPANDED,
+ cs.options & OPT_LINENUMBERS);
+ if (ret && (p.command & CMD_ZERO)) {
+ ret = nft_chain_zero_counters(h, p.chain,
+ p.table);
+ }
+ if (ret && (p.command & CMD_ZERO_NUM)) {
+ ret = nft_rule_zero_counters(h, p.chain, p.table,
+ p.rulenum - 1);
+ }
+ break;
+ case CMD_LIST_RULES:
+ case CMD_LIST_RULES|CMD_ZERO:
+ case CMD_LIST_RULES|CMD_ZERO_NUM:
+ ret = list_rules(h, p.chain, p.table, p.rulenum,
+ cs.options & OPT_VERBOSE);
+ if (ret && (p.command & CMD_ZERO)) {
+ ret = nft_chain_zero_counters(h, p.chain,
+ p.table);
+ }
+ if (ret && (p.command & CMD_ZERO_NUM)) {
+ ret = nft_rule_zero_counters(h, p.chain, p.table,
+ p.rulenum - 1);
+ }
+ break;
+ case CMD_NEW_CHAIN:
+ ret = nft_chain_user_add(h, p.chain, p.table);
+ break;
+ case CMD_DELETE_CHAIN:
+ ret = nft_chain_user_del(h, p.chain, p.table);
+ break;
+ case CMD_RENAME_CHAIN:
+ ret = nft_chain_user_rename(h, p.chain, p.table, p.newname);
+ break;
+ case CMD_SET_POLICY:
+ ret = nft_chain_set(h, p.table, p.chain, p.policy, NULL);
+ if (ret < 0)
+ xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
+ p.policy);
+ break;
+ default:
+ /* We should never reach this... */
+ exit_tryhelp(2);
+ }
+
+ *table = p.table;
+
+ xtables_rule_matches_free(&cs.matches);
+
+ 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);
+
+ return ret;
+}
diff --git a/iptables/xtables.lock b/iptables/xtables.lock
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/iptables/xtables.lock
diff --git a/libiptc/libiptc.c b/libiptc/libiptc.c
index 3a8e1431..d21c391e 100644
--- a/libiptc/libiptc.c
+++ b/libiptc/libiptc.c
@@ -599,7 +599,7 @@ static int iptcc_chain_index_rebuild(struct xtc_handle *h)
* There are different strategies, the simple and safe is to rebuild
* the chain index every time. The more advanced is to update the
* array index to point to the next element, but that requires some
- * house keeping and boundry checks. The advanced is implemented, as
+ * house keeping and boundary checks. The advanced is implemented, as
* the simple approach behaves badly when all chains are deleted
* because list_for_each processing will always hit the first chain
* index, thus causing a rebuild for every chain.
@@ -1364,7 +1364,7 @@ retry:
#ifdef IPTC_DEBUG2
{
int fd = open("/tmp/libiptc-so_get_entries.blob",
- O_CREAT|O_WRONLY);
+ O_CREAT|O_WRONLY, 0644);
if (fd >= 0) {
write(fd, h->entries, tmp);
close(fd);
@@ -2595,7 +2595,7 @@ TC_COMMIT(struct xtc_handle *handle)
#ifdef IPTC_DEBUG2
{
int fd = open("/tmp/libiptc-so_set_replace.blob",
- O_CREAT|O_WRONLY);
+ O_CREAT|O_WRONLY, 0644);
if (fd >= 0) {
write(fd, repl, sizeof(*repl) + repl->size);
close(fd);
@@ -2671,7 +2671,7 @@ TC_COMMIT(struct xtc_handle *handle)
#ifdef IPTC_DEBUG2
{
int fd = open("/tmp/libiptc-so_set_add_counters.blob",
- O_CREAT|O_WRONLY);
+ O_CREAT|O_WRONLY, 0644);
if (fd >= 0) {
write(fd, newcounters, counterlen);
close(fd);
diff --git a/libiptc/linux_list.h b/libiptc/linux_list.h
index abdcf88d..559e33c9 100644
--- a/libiptc/linux_list.h
+++ b/libiptc/linux_list.h
@@ -27,7 +27,7 @@
1; \
})
-#define prefetch(x) 1
+#define prefetch(x) ((void)0)
/* empty define to make this work in userspace -HW */
#define smp_wmb()
diff --git a/libxtables/xtables.c b/libxtables/xtables.c
index ef5bc072..d43f9706 100644
--- a/libxtables/xtables.c
+++ b/libxtables/xtables.c
@@ -13,7 +13,7 @@
*
* 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <ctype.h>
@@ -168,6 +168,19 @@ static const struct xtables_afinfo afinfo_ipv6 = {
.so_rev_target = IP6T_SO_GET_REVISION_TARGET,
};
+/* Dummy families for arptables-compat and ebtables-compat. Leave structure
+ * fields that we don't use unset.
+ */
+static const struct xtables_afinfo afinfo_bridge = {
+ .libprefix = "libebt_",
+ .family = NFPROTO_BRIDGE,
+};
+
+static const struct xtables_afinfo afinfo_arp = {
+ .libprefix = "libarpt_",
+ .family = NFPROTO_ARP,
+};
+
const struct xtables_afinfo *afinfo;
/* Search path for Xtables .so files */
@@ -224,6 +237,12 @@ void xtables_set_nfproto(uint8_t nfproto)
case NFPROTO_IPV6:
afinfo = &afinfo_ipv6;
break;
+ case NFPROTO_BRIDGE:
+ afinfo = &afinfo_bridge;
+ break;
+ case NFPROTO_ARP:
+ afinfo = &afinfo_arp;
+ break;
default:
fprintf(stderr, "libxtables: unhandled NFPROTO in %s\n",
__func__);
@@ -348,6 +367,11 @@ int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
modprobe = buf;
}
+ argv[0] = (char *)modprobe;
+ argv[1] = (char *)modname;
+ argv[2] = quiet ? "-q" : NULL;
+ argv[3] = NULL;
+
/*
* Need to flush the buffer, or the child may output it again
* when switching the program thru execv.
@@ -356,19 +380,10 @@ int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
switch (vfork()) {
case 0:
- argv[0] = (char *)modprobe;
- argv[1] = (char *)modname;
- if (quiet) {
- argv[2] = "-q";
- argv[3] = NULL;
- } else {
- argv[2] = NULL;
- argv[3] = NULL;
- }
execv(argv[0], argv);
/* not usually reached */
- exit(1);
+ _exit(1);
case -1:
free(buf);
return -1;
@@ -540,7 +555,7 @@ void xtables_parse_interface(const char *arg, char *vianame,
static void *load_extension(const char *search_path, const char *af_prefix,
const char *name, bool is_target)
{
- const char *all_prefixes[] = {"libxt_", af_prefix, NULL};
+ const char *all_prefixes[] = {af_prefix, "libxt_", NULL};
const char **prefix;
const char *dir = search_path, *next;
void *ptr = NULL;
@@ -578,8 +593,6 @@ static void *load_extension(const char *search_path, const char *af_prefix,
if (ptr != NULL)
return ptr;
- fprintf(stderr, "%s: no \"%s\" extension found for "
- "this protocol\n", path, name);
errno = ENOENT;
return NULL;
}
@@ -590,6 +603,16 @@ static void *load_extension(const char *search_path, const char *af_prefix,
}
#endif
+static bool extension_cmp(const char *name1, const char *name2, uint32_t family)
+{
+ if (strcmp(name1, name2) == 0 &&
+ (family == afinfo->family ||
+ family == NFPROTO_UNSPEC))
+ return true;
+
+ return false;
+}
+
struct xtables_match *
xtables_find_match(const char *name, enum xtables_tryload tryload,
struct xtables_rule_match **matches)
@@ -612,7 +635,7 @@ xtables_find_match(const char *name, enum xtables_tryload tryload,
/* Trigger delayed initialization */
for (dptr = &xtables_pending_matches; *dptr; ) {
- if (strcmp(name, (*dptr)->name) == 0) {
+ if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
ptr = *dptr;
*dptr = (*dptr)->next;
ptr->next = NULL;
@@ -623,7 +646,7 @@ xtables_find_match(const char *name, enum xtables_tryload tryload,
}
for (ptr = xtables_matches; ptr; ptr = ptr->next) {
- if (strcmp(name, ptr->name) == 0) {
+ if (extension_cmp(name, ptr->name, ptr->family)) {
struct xtables_match *clone;
/* First match of this type: */
@@ -673,7 +696,8 @@ xtables_find_match(const char *name, enum xtables_tryload tryload,
newentry = xtables_malloc(sizeof(struct xtables_rule_match));
for (i = matches; *i; i = &(*i)->next) {
- if (strcmp(name, (*i)->match->name) == 0)
+ if (extension_cmp(name, (*i)->match->name,
+ (*i)->match->family))
(*i)->completed = true;
}
newentry->match = ptr;
@@ -701,7 +725,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
/* Trigger delayed initialization */
for (dptr = &xtables_pending_targets; *dptr; ) {
- if (strcmp(name, (*dptr)->name) == 0) {
+ if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
ptr = *dptr;
*dptr = (*dptr)->next;
ptr->next = NULL;
@@ -712,7 +736,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
}
for (ptr = xtables_targets; ptr; ptr = ptr->next) {
- if (strcmp(name, ptr->name) == 0)
+ if (extension_cmp(name, ptr->name, ptr->family))
break;
}
@@ -745,7 +769,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);
@@ -801,12 +825,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)
@@ -1113,7 +1137,7 @@ void xtables_rule_matches_free(struct xtables_rule_match **matches)
*
* %XTF_BAD_VALUE: bad value for option
* @p1: module name
- * @p2: option with which the problem occured (e.g. "--mark")
+ * @p2: option with which the problem occurred (e.g. "--mark")
* @p3: string the user passed in (e.g. "99999999999999")
*
* %XTF_ONE_ACTION: two mutually exclusive actions have been specified
@@ -1186,13 +1210,20 @@ const char *xtables_ipaddr_to_numeric(const struct in_addr *addrp)
static const char *ipaddr_to_host(const struct in_addr *addr)
{
- struct hostent *host;
+ static char hostname[NI_MAXHOST];
+ struct sockaddr_in saddr = {
+ .sin_family = AF_INET,
+ .sin_addr = *addr,
+ };
+ int err;
+
- host = gethostbyaddr(addr, sizeof(struct in_addr), AF_INET);
- if (host == NULL)
+ err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in),
+ hostname, sizeof(hostname) - 1, NULL, 0, 0);
+ if (err != 0)
return NULL;
- return host->h_name;
+ return hostname;
}
static const char *ipaddr_to_network(const struct in_addr *addr)
@@ -1243,7 +1274,7 @@ const char *xtables_ipmask_to_numeric(const struct in_addr *mask)
uint32_t cidr;
cidr = xtables_ipmask_to_cidr(mask);
- if (cidr < 0) {
+ if (cidr == (unsigned int)-1) {
/* mask was not a decent combination of 1's and 0's */
sprintf(buf, "/%s", xtables_ipaddr_to_numeric(mask));
return buf;
@@ -1329,22 +1360,29 @@ static struct in_addr *network_to_ipaddr(const char *name)
static struct in_addr *host_to_ipaddr(const char *name, unsigned int *naddr)
{
- struct hostent *host;
struct in_addr *addr;
+ struct addrinfo hints;
+ struct addrinfo *res, *p;
+ int err;
unsigned int i;
- *naddr = 0;
- if ((host = gethostbyname(name)) != NULL) {
- if (host->h_addrtype != AF_INET ||
- host->h_length != sizeof(struct in_addr))
- return NULL;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_RAW;
- while (host->h_addr_list[*naddr] != NULL)
+ *naddr = 0;
+ if ((err = getaddrinfo(name, NULL, &hints, &res)) != 0) {
+ return NULL;
+ } else {
+ for (p = res; p != NULL; p = p->ai_next)
++*naddr;
addr = xtables_calloc(*naddr, sizeof(struct in_addr));
- for (i = 0; i < *naddr; i++)
- memcpy(&addr[i], host->h_addr_list[i],
+ for (i = 0, p = res; p != NULL; p = p->ai_next)
+ memcpy(&addr[i++],
+ &((const struct sockaddr_in *)p->ai_addr)->sin_addr,
sizeof(struct in_addr));
+ freeaddrinfo(res);
return addr;
}
@@ -1539,17 +1577,10 @@ static const char *ip6addr_to_host(const struct in6_addr *addr)
saddr.sin6_family = AF_INET6;
err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in6),
- hostname, sizeof(hostname) - 1, NULL, 0, 0);
- if (err != 0) {
-#ifdef DEBUG
- fprintf(stderr,"IP2Name: %s\n",gai_strerror(err));
-#endif
+ hostname, sizeof(hostname) - 1, NULL, 0, 0);
+ if (err != 0)
return NULL;
- }
-#ifdef DEBUG
- fprintf (stderr, "\naddr2host: %s\n", hostname);
-#endif
return hostname;
}
@@ -1612,9 +1643,7 @@ struct in6_addr *xtables_numeric_to_ip6addr(const char *num)
if ((err = inet_pton(AF_INET6, num, &ap)) == 1)
return &ap;
-#ifdef DEBUG
- fprintf(stderr, "\nnumeric2addr: %d\n", err);
-#endif
+
return NULL;
}
@@ -1634,18 +1663,11 @@ host_to_ip6addr(const char *name, unsigned int *naddr)
*naddr = 0;
if ((err = getaddrinfo(name, NULL, &hints, &res)) != 0) {
-#ifdef DEBUG
- fprintf(stderr,"Name2IP: %s\n",gai_strerror(err));
-#endif
return NULL;
} else {
/* Find length of address chain */
for (p = res; p != NULL; p = p->ai_next)
++*naddr;
-#ifdef DEBUG
- fprintf(stderr, "resolved: len=%d %s ", res->ai_addrlen,
- xtables_ip6addr_to_numeric(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr));
-#endif
/* Copy each element of the address chain */
addr = xtables_calloc(*naddr, sizeof(struct in6_addr));
for (i = 0, p = res; p != NULL; p = p->ai_next)
@@ -1704,8 +1726,9 @@ static struct in6_addr *parse_ip6mask(char *mask)
if (bits != 0) {
char *p = (void *)&maskaddr;
memset(p, 0xff, bits / 8);
- memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
- p[bits/8] = 0xff << (8 - (bits & 7));
+ memset(p + ((bits + 7) / 8), 0, (128 - bits) / 8);
+ if (bits < 128)
+ p[bits/8] = 0xff << (8 - (bits & 7));
return &maskaddr;
}
@@ -1961,3 +1984,72 @@ void get_kernel_version(void)
sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
kernel_version = LINUX_VERSION(x, y, z);
}
+
+#include <linux/netfilter/nf_tables.h>
+
+struct xt_xlate {
+ struct {
+ char *data;
+ int size;
+ int rem;
+ int off;
+ } buf;
+ char comment[NFT_USERDATA_MAXLEN];
+};
+
+struct xt_xlate *xt_xlate_alloc(int size)
+{
+ struct xt_xlate *xl;
+
+ xl = malloc(sizeof(struct xt_xlate));
+ if (xl == NULL)
+ xtables_error(RESOURCE_PROBLEM, "OOM");
+
+ xl->buf.data = malloc(size);
+ if (xl->buf.data == NULL)
+ xtables_error(RESOURCE_PROBLEM, "OOM");
+
+ xl->buf.size = size;
+ xl->buf.rem = size;
+ xl->buf.off = 0;
+ xl->comment[0] = '\0';
+
+ return xl;
+}
+
+void xt_xlate_free(struct xt_xlate *xl)
+{
+ free(xl->buf.data);
+ free(xl);
+}
+
+void xt_xlate_add(struct xt_xlate *xl, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, fmt);
+ len = vsnprintf(xl->buf.data + xl->buf.off, xl->buf.rem, fmt, ap);
+ if (len < 0 || len >= xl->buf.rem)
+ xtables_error(RESOURCE_PROBLEM, "OOM");
+
+ va_end(ap);
+ xl->buf.rem -= len;
+ xl->buf.off += len;
+}
+
+void xt_xlate_add_comment(struct xt_xlate *xl, const char *comment)
+{
+ strncpy(xl->comment, comment, NFT_USERDATA_MAXLEN - 1);
+ xl->comment[NFT_USERDATA_MAXLEN - 1] = '\0';
+}
+
+const char *xt_xlate_get_comment(struct xt_xlate *xl)
+{
+ return xl->comment[0] ? xl->comment : NULL;
+}
+
+const char *xt_xlate_get(struct xt_xlate *xl)
+{
+ return xl->buf.data;
+}
diff --git a/libxtables/xtoptions.c b/libxtables/xtoptions.c
index 78e9abd6..d26d2f8b 100644
--- a/libxtables/xtoptions.c
+++ b/libxtables/xtoptions.c
@@ -519,7 +519,7 @@ static void xtopt_parse_host(struct xt_option_call *cb)
int ret;
ret = getaddrinfo(cb->arg, NULL, &hints, &res);
- if (ret < 0)
+ if (ret != 0)
xt_params->exit_err(PARAMETER_PROBLEM,
"getaddrinfo: %s\n", gai_strerror(ret));
@@ -562,7 +562,7 @@ static int xtables_getportbyname(const char *name)
int ret;
ret = getaddrinfo(NULL, name, NULL, &res);
- if (ret < 0)
+ if (ret != 0)
return -1;
ret = -1;
for (p = res; p != NULL; p = p->ai_next) {
@@ -675,7 +675,7 @@ static int xtopt_parse_mask(struct xt_option_call *cb)
int ret;
ret = getaddrinfo(cb->arg, NULL, &hints, &res);
- if (ret < 0)
+ if (ret != 0)
return 0;
memcpy(&cb->val.hmask, xtables_sa_host(res->ai_addr, res->ai_family),
@@ -802,7 +802,7 @@ static void xtopt_parse_ethermac(struct xt_option_call *cb)
sizeof(cb->val.ethermac));
return;
out:
- xt_params->exit_err(PARAMETER_PROBLEM, "ether");
+ xt_params->exit_err(PARAMETER_PROBLEM, "Invalid MAC address specified.");
}
static void (*const xtopt_subparse[])(struct xt_option_call *) = {
diff --git a/tests/options-ipv4.rules b/tests/options-ipv4.rules
deleted file mode 100644
index b4adc926..00000000
--- a/tests/options-ipv4.rules
+++ /dev/null
@@ -1,52 +0,0 @@
-# Generated by iptables-save v1.4.10 on Mon Jan 31 03:03:38 2011
-*mangle
-:PREROUTING ACCEPT [2461:977932]
-:INPUT ACCEPT [2461:977932]
-:FORWARD ACCEPT [0:0]
-:OUTPUT ACCEPT [1740:367048]
-:POSTROUTING ACCEPT [1740:367048]
-
-# libipt_
--A INPUT -p ah -m ah --ahspi 1
--A INPUT -p ah -m ah --ahspi :2
--A INPUT -p ah -m ah --ahspi 0:3
--A INPUT -p ah -m ah --ahspi 4:
--A INPUT -p ah -m ah --ahspi 5:4294967295
-
--A FORWARD -p tcp -j ECN --ecn-tcp-remove
--A FORWARD -j LOG --log-prefix "hi" --log-tcp-sequence --log-tcp-options --log-ip-options --log-uid --log-macdecode
--A FORWARD -j TTL --ttl-inc 1
--A FORWARD -j TTL --ttl-dec 1
--A FORWARD -j TTL --ttl-set 1
--A FORWARD -j ULOG --ulog-prefix "abc" --ulog-cprange 2 --ulog-qthreshold 2
-COMMIT
-# Completed on Mon Jan 31 03:03:38 2011
-# Generated by iptables-save v1.4.10 on Mon Jan 31 03:03:38 2011
-*nat
-:PREROUTING ACCEPT [0:0]
-:INPUT ACCEPT [0:0]
-:OUTPUT ACCEPT [0:0]
-:POSTROUTING ACCEPT [0:0]
--A PREROUTING -d 1.2.3.4/32 -i lo -j CLUSTERIP --new --hashmode sourceip --clustermac 01:02:03:04:05:06 --total-nodes 9 --local-node 2 --hash-init 123456789
--A PREROUTING -i dummy0 -j DNAT --to-destination 1.2.3.4 --random --persistent
--A PREROUTING -i dummy0 -p tcp -j REDIRECT --to-ports 1-2 --random
--A POSTROUTING -o dummy0 -p tcp -j MASQUERADE --to-ports 1-2 --random
--A POSTROUTING -o dummy0 -p tcp -j NETMAP --to 1.0.0.0/8
--A POSTROUTING -o dummy0 -p tcp -j SNAT --to-source 1.2.3.4-1.2.3.5 --random --persistent
-COMMIT
-# Completed on Mon Jan 31 03:03:38 2011
-# Generated by iptables-save v1.4.10 on Mon Jan 31 03:03:38 2011
-*filter
-:INPUT ACCEPT [76:13548]
-:FORWARD ACCEPT [0:0]
-:OUTPUT ACCEPT [59:11240]
-#-A INPUT -m addrtype --src-type UNICAST --dst-type UNICAST --limit-iface-in
--A INPUT -p tcp -m ecn --ecn-tcp-ece --ecn-tcp-cwr --ecn-ip-ect 0
--A INPUT -p tcp -m ecn --ecn-tcp-ece --ecn-tcp-cwr --ecn-ip-ect 1
--A INPUT -p icmp -m icmp --icmp-type 5/0
--A INPUT -p icmp -m icmp --icmp-type 5/1
--A INPUT -p icmp -m icmp --icmp-type 5
--A INPUT -m realm --realm 0x1 -m ttl --ttl-eq 64 -m ttl --ttl-lt 64 -m ttl --ttl-gt 64
--A FORWARD -p tcp -j REJECT --reject-with tcp-reset
-COMMIT
-# Completed on Mon Jan 31 03:03:39 2011
diff --git a/tests/options-most.rules b/tests/options-most.rules
deleted file mode 100644
index ef4e7f1c..00000000
--- a/tests/options-most.rules
+++ /dev/null
@@ -1,214 +0,0 @@
-*filter
-:INPUT ACCEPT [0:0]
-:FORWARD ACCEPT [0:0]
-:OUTPUT ACCEPT [0:0]
-:matches - -
-:ntarg - -
-:zmatches - -
--A INPUT -j matches
--A INPUT -m u32 --u32 "0x0=0x0&&0x0=0x1" -j ntarg
--A INPUT -j zmatches
--A INPUT -m conntrack --ctstate INVALID --ctproto 6 --ctorigsrc fe80::/64 --ctorigdst fe80::/64 --ctreplsrc fe80::/64 --ctrepldst fe80::/64 --ctorigsrcport 12 --ctorigdstport 13 --ctreplsrcport 14 --ctrepldstport 15 --ctstatus EXPECTED --ctexpire 1:2 --ctdir REPLY
--A INPUT -p tcp -m cluster --cluster-local-nodemask 0x00000001 --cluster-total-nodes 2 --cluster-hash-seed 0x00000001 -m cluster --cluster-local-nodemask 0x00000001 --cluster-total-nodes 2 --cluster-hash-seed 0x00000001 -m comment --comment foo -m connbytes --connbytes 1:2 --connbytes-mode packets --connbytes-dir both -m connlimit --connlimit-upto 1 --connlimit-mask 8 --connlimit-saddr -m connlimit --connlimit-above 1 --connlimit-mask 9 --connlimit-daddr -m connmark --mark 0x99 -m conntrack --ctstate INVALID --ctproto 6 --ctorigsrc fe80::/64 --ctorigdst fe80::/64 --ctreplsrc fe80::/64 --ctrepldst fe80::/64 --ctorigsrcport 12 --ctorigdstport 13 --ctreplsrcport 14 --ctrepldstport 15 --ctstatus EXPECTED --ctexpire 1:2 --ctdir REPLY -m cpu --cpu 2 -m dscp --dscp 0x04 -m dscp --dscp 0x00 -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 5 --hashlimit-mode srcip,dstip --hashlimit-name f1 --hashlimit-htable-size 64 --hashlimit-htable-max 128 --hashlimit-htable-gcinterval 60 --hashlimit-htable-expire 120 --hashlimit-srcmask 24 --hashlimit-dstmask 24 -m hashlimit --hashlimit-above 5/sec --hashlimit-burst 5 --hashlimit-name f1 -m helper --helper ftp -m iprange --src-range ::1-::2 --dst-range ::1-::2 -m ipvs --vaddr fe80::/64 --vport 1 --vdir REPLY --vmethod GATE --vportctl 21 -m length --length 1:2 -m limit --limit 1/sec -m mac --mac-source 01:02:03:04:05:06 -m mark --mark 0x1 -m physdev --physdev-in eth0 -m pkttype --pkt-type unicast -m policy --dir in --pol ipsec --strict --reqid 1 --spi 0x1 --proto esp --mode tunnel --tunnel-dst fe80::/64 --tunnel-src fe80::/64 --next --reqid 2 -m quota --quota 0 -m recent --rcheck --name DEFAULT --rsource -m socket --transparent -m string --string "foobar" --algo kmp --from 1 --to 2 --icase -m time --timestart 01:02:03 --timestop 03:04:05 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05 --utc -m tos --tos 0xff/0x01 -m u32 --u32 "0x0=0x0" -m u32 --u32 "0x0=0x0" -m hbh -m hbh -m hl --hl-eq 1
--A INPUT -m ipv6header --header hop-by-hop --soft
--A INPUT -p tcp -m cluster --cluster-local-nodemask 0x00000001 --cluster-total-nodes 2 --cluster-hash-seed 0x00000001
--A INPUT -p tcp -m cluster --cluster-local-nodemask 0x00000001 --cluster-total-nodes 2 --cluster-hash-seed 0x00000001
--A INPUT -p tcp -m comment --comment foo
--A INPUT -p tcp -m connbytes --connbytes 1:2 --connbytes-mode packets --connbytes-dir both
--A INPUT -p tcp -m connlimit --connlimit-upto 1 --connlimit-mask 8 --connlimit-saddr
--A INPUT -p tcp -m connlimit --connlimit-above 1 --connlimit-mask 9 --connlimit-daddr
--A INPUT -p tcp -m connmark --mark 0x99
--A INPUT -p tcp -m conntrack --ctstate INVALID --ctproto 6 --ctorigsrc fe80::/64 --ctorigdst fe80::/64 --ctreplsrc fe80::/64 --ctrepldst fe80::/64 --ctorigsrcport 12 --ctorigdstport 13 --ctreplsrcport 14 --ctrepldstport 15 --ctstatus EXPECTED --ctexpire 1:2 --ctdir REPLY
--A INPUT -p tcp -m cpu --cpu 2
--A INPUT -p tcp -m dscp --dscp 0x04 -m dscp ! --dscp 0x04
--A INPUT -p tcp -m dscp --dscp 0x00 -m dscp ! --dscp 0x00
--A INPUT -p tcp -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 5 --hashlimit-mode srcip,dstip --hashlimit-name f1 --hashlimit-htable-size 64 --hashlimit-htable-max 128 --hashlimit-htable-gcinterval 60 --hashlimit-htable-expire 120 --hashlimit-srcmask 24 --hashlimit-dstmask 24
--A INPUT -p tcp -m hashlimit --hashlimit-above 5/sec --hashlimit-burst 5 --hashlimit-name f1
--A INPUT -p tcp -m helper --helper ftp
--A INPUT -p tcp -m iprange --src-range ::1-::2 --dst-range ::1-::2
--A INPUT -p tcp -m length --length 1:2
--A INPUT -p tcp -m limit --limit 1/sec
--A INPUT -p tcp -m mac --mac-source 01:02:03:04:05:06
--A INPUT -p tcp -m mark --mark 0x1
--A INPUT -p tcp -m physdev --physdev-in eth0
--A INPUT -p tcp -m pkttype --pkt-type unicast
--A INPUT -p tcp -m policy --dir in --pol ipsec --strict --reqid 1 --spi 0x1 --proto esp --mode tunnel --tunnel-dst fe80::/64 --tunnel-src fe80::/64 --next --reqid 2
--A INPUT -p tcp -m quota --quota 0
--A INPUT -p tcp -m recent --rcheck --name DEFAULT --rsource
--A INPUT -p tcp -m socket --transparent
--A INPUT -p tcp -m string --string "foobar" --algo kmp --from 1 --to 2 --icase
--A INPUT -p tcp -m string --hex-string "|00|" --algo kmp --from 1 --to 2 --icase
--A INPUT -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN
--A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN
--A INPUT -p tcp -m tos --tos 0xff/0x01
--A INPUT -p tcp -m u32 ! --u32 "0x0=0x0" -m u32 ! --u32 "0x0=0x0"
--A INPUT -p tcp -m hbh -m hbh -m hl --hl-eq 1 -m ipv6header --header hop-by-hop --soft
--A INPUT -m ipv6header --header hop-by-hop --soft -m rt --rt-type 2 --rt-segsleft 2 --rt-len 5 -m rt --rt-type 0 --rt-segsleft 2 --rt-len 5 --rt-0-res --rt-0-addrs ::1 --rt-0-not-strict -m rt --rt-type 0 --rt-segsleft 2 --rt-len 5 --rt-0-res --rt-0-addrs ::1,::2 --rt-0-not-strict
--A INPUT -p tcp -m cpu --cpu 1 -m tcp --sport 1:2 --dport 1:2 --tcp-option 1 --tcp-flags FIN,SYN,RST,ACK SYN -m cpu --cpu 1
--A INPUT -p dccp -m cpu --cpu 1 -m dccp --sport 1:2 --dport 3:4 -m cpu --cpu 1
--A INPUT -p dccp -m dccp ! --sport 1:2 ! --dport 3:4 ! --dccp-types REQUEST,RESPONSE ! --dccp-option 1
--A INPUT -p udp -m cpu --cpu 1 -m udp --sport 1:2 --dport 3:4 -m cpu --cpu 1
--A INPUT -p sctp -m cpu --cpu 1 -m sctp --sport 1:2 --dport 3:4 --chunk-types all INIT,SACK -m cpu --cpu 1
--A INPUT -p esp -m esp --espspi 1:2
--A INPUT -p tcp -m multiport --dports 1,2 -m multiport --dports 1,2
--A INPUT -p tcp -m tcpmss --mss 1:2 -m tcp --tcp-flags FIN,SYN,RST,ACK SYN
--A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 4/0
--A INPUT
--A INPUT -p ipv6-mh -m mh --mh-type 3
--A OUTPUT -m owner --socket-exists --uid-owner 1-2 --gid-owner 2-3
--A OUTPUT -m owner ! --socket-exists ! --uid-owner 0 ! --gid-owner 0
--A matches -m connbytes --connbytes 1 --connbytes-mode bytes --connbytes-dir both
--A matches
--A matches -m connbytes --connbytes :2 --connbytes-mode bytes --connbytes-dir both
--A matches
--A matches -m connbytes --connbytes 0:3 --connbytes-mode bytes --connbytes-dir both
--A matches
--A matches -m connbytes --connbytes 4: --connbytes-mode bytes --connbytes-dir both
--A matches
--A matches -m connbytes --connbytes 5:18446744073709551615 --connbytes-mode bytes --connbytes-dir both
--A matches
--A matches -m conntrack --ctexpire 1
--A matches
--A matches -m conntrack --ctexpire :2
--A matches
--A matches -m conntrack --ctexpire 0:3
--A matches
--A matches -m conntrack --ctexpire 4:
--A matches
--A matches -m conntrack --ctexpire 5:4294967295
--A matches
--A matches -m conntrack ! --ctstate NEW ! --ctproto tcp ! --ctorigsrc ::1/127 ! --ctorigdst ::2/127 ! --ctreplsrc ::2/127 ! --ctrepldst ::2/127 ! --ctorigsrcport 3 ! --ctorigdstport 4 ! --ctreplsrcport 5 ! --ctrepldstport 6 ! --ctstatus ASSURED ! --ctexpire 8:9
--A matches
--A matches -m dst ! --dst-len 12
--A matches
--A matches -p esp -m esp --espspi 1
--A matches
--A matches -p esp -m esp --espspi :2
--A matches
--A matches -p esp -m esp --espspi 0:3
--A matches
--A matches -p esp -m esp --espspi 4:
--A matches
--A matches -p esp -m esp --espspi 5:4294967295
--A matches
--A matches -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-name mini1 --hashlimit-htable-expire 2000
--A matches -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-name mini1
--A matches -m hashlimit --hashlimit-upto 1/min --hashlimit-burst 1 --hashlimit-name mini2
--A matches -m hashlimit --hashlimit-upto 1/hour --hashlimit-burst 1 --hashlimit-name mini3
--A matches -m hashlimit --hashlimit-upto 1/day --hashlimit-burst 1 --hashlimit-name mini4
--A matches -m hashlimit --hashlimit-upto 4kb/s --hashlimit-burst 400kb --hashlimit-name mini5
--A matches -m hashlimit --hashlimit-upto 10mb/s --hashlimit-name mini6
--A matches -m hashlimit --hashlimit-upto 123456b/s --hashlimit-burst 1mb --hashlimit-name mini7
--A matches
--A matches -m hbh ! --hbh-len 5
--A matches
--A matches -m ipvs --vaddr fe80::/64 --vport 1 --vdir REPLY --vmethod GATE --vportctl 21
--A matches
--A matches -m length --length 1
--A matches
--A matches -m length --length :2
--A matches
--A matches -m length --length 0:3
--A matches
--A matches -m length --length 4:
--A matches
--A matches -m length --length 5:65535
--A matches
--A matches -m physdev ! --physdev-is-in ! --physdev-is-out ! --physdev-is-bridged
--A matches
--A matches -p tcp -m tcpmss --mss 1
--A matches
--A matches -p tcp -m tcpmss --mss :2
--A matches
--A matches -p tcp -m tcpmss --mss 0:3
--A matches
--A matches -p tcp -m tcpmss --mss 4:
--A matches
--A matches -p tcp -m tcpmss --mss 5:65535
--A matches
--A matches -m statistic --mode random ! --probability 0.4
--A matches
--A matches -m statistic --mode nth ! --every 5 --packet 2
--A matches
--A matches -m string --hex-string "action=|5C22|http|3A|" --algo bm
--A matches
--A matches -m string --hex-string "action=|5C|http|3A|" --algo bm
--A matches
--A matches -m time --timestart 01:02:03 --timestop 04:05:06 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05 --localtz
--A matches
--A matches -m time --timestart 01:02:03 --timestop 04:05:06 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05 --kerneltz
--A matches
--A matches -m time --timestart 01:02:03 --timestop 04:05:06 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05
--A matches
--A matches -m time --timestart 02:00:00 --timestop 03:00:00 --datestart 1970-01-01T02:00:00 --datestop 1970-01-01T03:00:00
--A matches
--A matches -m ah --ahspi 1
--A matches
--A matches -m ah --ahspi :2
--A matches
--A matches -m ah --ahspi 0:3
--A matches
--A matches -m ah --ahspi 4:
--A matches
--A matches -m ah --ahspi 5:4294967295
--A matches
--A matches -m frag --fragid 1
--A matches
--A matches -m frag --fragid :2
--A matches
--A matches -m frag --fragid 0:3
--A matches
--A matches -m frag --fragid 4:
--A matches
--A matches -m frag --fragid 5:4294967295
--A matches
--A matches -m frag ! --fragid 9:10 ! --fraglen 12
--A matches
--A matches -m rt --rt-segsleft 1
--A matches
--A matches -m rt --rt-segsleft :2
--A matches
--A matches -m rt --rt-segsleft 0:3
--A matches
--A matches -m rt --rt-segsleft 4:
--A matches
--A matches -m rt --rt-segsleft 5:4294967295
--A matches
--A ntarg -j LOG --log-tcp-sequence --log-tcp-options --log-ip-options
--A ntarg
--A ntarg -j NFQUEUE --queue-num 1
--A ntarg
--A ntarg -j NFQUEUE --queue-balance 8:99
--A ntarg
--A ntarg -j NFQUEUE --queue-num 0 --queue-bypass
--A ntarg
--A ntarg -j RATEEST --rateest-name RE1 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms
--A ntarg
--A ntarg -j RATEEST --rateest-name RE2 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms
--A ntarg
--A zmatches -m rateest --rateest RE1 --rateest-lt --rateest-bps 8bit
--A zmatches -m rateest --rateest RE1 --rateest-eq --rateest-pps 5
--A zmatches -m rateest --rateest RE1 --rateest-gt --rateest-bps 5kbit
--A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-lt --rateest-bps2 16bit
--A zmatches -m rateest --rateest1 RE1 --rateest-lt --rateest-bps --rateest2 RE2
--A zmatches -m rateest --rateest-delta --rateest1 RE1 --rateest-lt --rateest2 RE2 --rateest-pps2 42
--A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-eq --rateest-bps2 16bit
--A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-gt --rateest-bps2 16bit
--A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-lt --rateest-pps2 9
--A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-eq --rateest-pps2 9
--A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-gt --rateest-pps2 9
-COMMIT
-*mangle
-:PREROUTING ACCEPT [0:0]
-:INPUT ACCEPT [0:0]
-:FORWARD ACCEPT [0:0]
-:OUTPUT ACCEPT [0:0]
-:POSTROUTING ACCEPT [0:0]
-:matches - -
-:ntarg - -
-:zmatches - -
--A INPUT -m u32 --u32 "0x0=0x0&&0x0=0x1" -j ntarg
--A ntarg -j HL --hl-inc 1
--A ntarg -j HL --hl-dec 1
--A ntarg
-COMMIT
diff --git a/utils/Makefile.am b/utils/Makefile.am
index c26aa640..c4192a9e 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -18,3 +18,8 @@ if ENABLE_BPFC
sbin_PROGRAMS += nfbpf_compile
nfbpf_compile_LDADD = -lpcap
endif
+
+if ENABLE_SYNCONF
+sbin_PROGRAMS += nfsynproxy
+nfsynproxy_LDADD = -lpcap
+endif
diff --git a/utils/nfnl_osf.c b/utils/nfnl_osf.c
index bb5f92dc..645ec648 100644
--- a/utils/nfnl_osf.c
+++ b/utils/nfnl_osf.c
@@ -14,7 +14,7 @@
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sys/types.h>
diff --git a/utils/nfsynproxy.c b/utils/nfsynproxy.c
new file mode 100644
index 00000000..baedc92c
--- /dev/null
+++ b/utils/nfsynproxy.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pcap/pcap.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+static const char *iface = "lo";
+static uint16_t port;
+static const char *chain = "SYNPROXY";
+
+static int parse_packet(const char *host, const uint8_t *data)
+{
+ const struct iphdr *iph = (void *)data + 14;
+ const struct tcphdr *th = (void *)iph + iph->ihl * 4;
+ int length;
+ uint8_t *ptr;
+
+ if (!th->syn || !th->ack)
+ return 0;
+
+ printf("-A %s -d %s -p tcp --dport %u "
+ "-m state --state UNTRACKED,INVALID "
+ "-j SYNPROXY ", chain, host, port);
+
+ /* ECE && !CWR */
+ if (th->res2 == 0x1)
+ printf("--ecn ");
+
+ length = th->doff * 4 - sizeof(*th);
+ ptr = (uint8_t *)(th + 1);
+ while (length > 0) {
+ int opcode = *ptr++;
+ int opsize;
+
+ switch (opcode) {
+ case TCPOPT_EOL:
+ return 1;
+ case TCPOPT_NOP:
+ length--;
+ continue;
+ default:
+ opsize = *ptr++;
+ if (opsize < 2)
+ return 1;
+ if (opsize > length)
+ return 1;
+
+ switch (opcode) {
+ case TCPOPT_MAXSEG:
+ if (opsize == TCPOLEN_MAXSEG)
+ printf("--mss %u ", ntohs(*(uint16_t *)ptr));
+ break;
+ case TCPOPT_WINDOW:
+ if (opsize == TCPOLEN_WINDOW)
+ printf("--wscale %u ", *ptr);
+ break;
+ case TCPOPT_TIMESTAMP:
+ if (opsize == TCPOLEN_TIMESTAMP)
+ printf("--timestamp ");
+ break;
+ case TCPOPT_SACK_PERMITTED:
+ if (opsize == TCPOLEN_SACK_PERMITTED)
+ printf("--sack-perm ");
+ break;
+ }
+
+ ptr += opsize - 2;
+ length -= opsize;
+ }
+ }
+ printf("\n");
+ return 1;
+}
+
+static void probe_host(const char *host)
+{
+ struct sockaddr_in sin;
+ char pcap_errbuf[PCAP_ERRBUF_SIZE];
+ struct pcap_pkthdr pkthdr;
+ const uint8_t *data;
+ struct bpf_program fp;
+ pcap_t *ph;
+ int fd;
+
+ ph = pcap_create(iface, pcap_errbuf);
+ if (ph == NULL) {
+ perror("pcap_create");
+ goto err1;
+ }
+
+ if (pcap_setnonblock(ph, 1, pcap_errbuf) == -1) {
+ perror("pcap_setnonblock");
+ goto err2;
+ }
+
+ if (pcap_setfilter(ph, &fp) == -1) {
+ pcap_perror(ph, "pcap_setfilter");
+ goto err2;
+ }
+
+ if (pcap_activate(ph) != 0) {
+ pcap_perror(ph, "pcap_activate");
+ goto err2;
+ }
+
+ if (pcap_compile(ph, &fp, "src host 127.0.0.1 and tcp and src port 80",
+ 1, PCAP_NETMASK_UNKNOWN) == -1) {
+ pcap_perror(ph, "pcap_compile");
+ goto err2;
+ }
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ goto err3;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ sin.sin_addr.s_addr = inet_addr(host);
+
+ if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("connect");
+ goto err4;
+ }
+
+ for (;;) {
+ data = pcap_next(ph, &pkthdr);
+ if (data == NULL)
+ break;
+ if (parse_packet(host, data))
+ break;
+ }
+
+ close(fd);
+
+err4:
+ close(fd);
+err3:
+ pcap_freecode(&fp);
+err2:
+ pcap_close(ph);
+err1:
+ return;
+}
+
+enum {
+ OPT_HELP = 'h',
+ OPT_IFACE = 'i',
+ OPT_PORT = 'p',
+ OPT_CHAIN = 'c',
+};
+
+static const struct option options[] = {
+ { .name = "help", .has_arg = false, .val = OPT_HELP },
+ { .name = "iface", .has_arg = true, .val = OPT_IFACE },
+ { .name = "port" , .has_arg = true, .val = OPT_PORT },
+ { .name = "chain", .has_arg = true, .val = OPT_CHAIN },
+ { }
+};
+
+static void print_help(const char *name)
+{
+ printf("%s [ options ] address...\n"
+ "\n"
+ "Options:\n"
+ " -i/--iface Outbound interface\n"
+ " -p/--port Port number to probe\n"
+ " -c/--chain Chain name to use for rules\n"
+ " -h/--help Show this help\n",
+ name);
+}
+
+int main(int argc, char **argv)
+{
+ int optidx = 0, c;
+
+ for (;;) {
+ c = getopt_long(argc, argv, "hi:p:c:", options, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case OPT_IFACE:
+ iface = optarg;
+ break;
+ case OPT_PORT:
+ port = atoi(optarg);
+ break;
+ case OPT_CHAIN:
+ chain = optarg;
+ break;
+ case OPT_HELP:
+ print_help(argv[0]);
+ exit(0);
+ case '?':
+ print_help(argv[0]);
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ while (argc > 0) {
+ probe_host(*argv);
+ argc--;
+ argv++;
+ }
+ return 0;
+}
diff --git a/utils/pf.os b/utils/pf.os
index 44e00146..01fb85be 100644
--- a/utils/pf.os
+++ b/utils/pf.os
@@ -1,4 +1,5 @@
-# $OpenBSD: pf.os,v 1.20 2006/06/02 16:54:34 david Exp $
+# $FreeBSD: head/etc/pf.os 258865 2013-12-03 04:32:02Z eadler $
+# $OpenBSD: pf.os,v 1.26 2012/08/03 12:25:16 jsg Exp $
# passive OS fingerprinting
# -------------------------
#
@@ -225,7 +226,13 @@ S2:64:1:60:M*,S,T,N,W0: Linux:2.4::Linux 2.4 (big boy)
S3:64:1:60:M*,S,T,N,W0: Linux:2.4:.18-21:Linux 2.4.18 and newer
S4:64:1:60:M*,S,T,N,W0: Linux:2.4::Linux 2.4/2.6 <= 2.6.7
S4:64:1:60:M*,S,T,N,W0: Linux:2.6:.1-7:Linux 2.4/2.6 <= 2.6.7
-S4:64:1:60:M*,S,T,N,W7: Linux:2.6:8:Linux 2.6.8 and newer (?)
+
+S4:64:1:60:M*,S,T,N,W5: Linux:2.6::Linux 2.6 (newer, 1)
+S4:64:1:60:M*,S,T,N,W6: Linux:2.6::Linux 2.6 (newer, 2)
+S4:64:1:60:M*,S,T,N,W7: Linux:2.6::Linux 2.6 (newer, 3)
+T4:64:1:60:M*,S,T,N,W7: Linux:2.6::Linux 2.6 (newer, 4)
+
+S10:64:1:60:M*,S,T,N,W4: Linux:3.0::Linux 3.0
S3:64:1:60:M*,S,T,N,W1: Linux:2.5::Linux 2.5 (sometimes 2.4)
S4:64:1:60:M*,S,T,N,W1: Linux:2.5-2.6::Linux 2.5/2.6
@@ -298,12 +305,23 @@ S22:64:1:52:M*,N,N,S,N,W0: Linux:2.2:ts:Linux 2.2 w/o timestamps
# ----------------- OpenBSD -----------------
16384:64:0:60:M*,N,W0,N,N,T: OpenBSD:2.6::NetBSD 1.3 (or OpenBSD 2.6)
-16384:64:1:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.0-3.9::OpenBSD 3.0-3.9
-16384:64:0:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.0-3.9:no-df:OpenBSD 3.0-3.9 (scrub no-df)
-57344:64:1:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.3-3.9::OpenBSD 3.3-3.9
-57344:64:0:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.3-3.9:no-df:OpenBSD 3.3-3.9 (scrub no-df)
+16384:64:1:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.0-4.8::OpenBSD 3.0-4.8
+16384:64:0:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.0-4.8:no-df:OpenBSD 3.0-4.8 (scrub no-df)
+57344:64:1:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.3-4.0::OpenBSD 3.3-4.0
+57344:64:0:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.3-4.0:no-df:OpenBSD 3.3-4.0 (scrub no-df)
+
+65535:64:1:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.0-4.0:opera:OpenBSD 3.0-4.0 (Opera)
-65535:64:1:64:M*,N,N,S,N,W0,N,N,T: OpenBSD:3.0-3.9:opera:OpenBSD 3.0-3.9 (Opera)
+16384:64:1:64:M*,N,N,S,N,W3,N,N,T: OpenBSD:4.9::OpenBSD 4.9
+16384:64:0:64:M*,N,N,S,N,W3,N,N,T: OpenBSD:4.9:no-df:OpenBSD 4.9 (scrub no-df)
+
+# ----------------- DragonFly BSD -----------------
+
+57344:64:1:60:M*,N,W0,N,N,T: DragonFly:1.0:A:DragonFly 1.0A
+57344:64:0:64:M*,N,W0,N,N,S,N,N,T: DragonFly:1.2-1.12::DragonFly 1.2-1.12
+5840:64:1:60:M*,S,T,N,W4: DragonFly:2.0-2.1::DragonFly 2.0-2.1
+57344:64:0:64:M*,N,W0,N,N,S,N,N,T: DragonFly:2.2-2.3::DragonFly 2.2-2.3
+57344:64:0:64:M*,N,W5,N,N,S,N,N,T: DragonFly:2.4-2.7::DragonFly 2.4-2.7
# ----------------- Solaris -----------------
@@ -356,13 +374,12 @@ S34:64:1:52:M*,N,W0,N,N,S: Solaris:10:beta:Solaris 10 (beta)
16616:255:1:48:M*,N,N,N: MacOS:8.1-8.6:OTTCP:MacOS 8.1-8.6 (OTTCP)
32768:255:1:48:M*,W0,N: MacOS:9.0-9.2::MacOS 9.0-9.2
65535:255:1:48:M*,N,N,N,N: MacOS:9.1::MacOS 9.1 (OT 2.7.4)
-65535:64:1:64:M*,N,W0,N,N,T,S,E,E: MacOS:10::MacOS X
# ----------------- Windows -----------------
# Windows TCP/IP stack is a mess. For most recent XP, 2000 and
-# even 98, the pathlevel, not the actual OS version, is more
+# even 98, the patchlevel, not the actual OS version, is more
# relevant to the signature. They share the same code, so it would
# seem. Luckily for us, almost all Windows 9x boxes have an
# awkward MSS of 536, which I use to tell one from another
@@ -426,6 +443,8 @@ S44:128:1:48:M*,N,N,S: Windows:XP:SP1:Windows Pro SP1, 2000 SP3
32767:128:1:48:M*,N,N,S: Windows:2000:SP4:Windows SP1, 2000 SP4
32767:128:1:48:M*,N,N,S: Windows:XP:SP1:Windows SP1, 2000 SP4
+8192:128:1:52:M*,N,W2,N,N,S: Windows:Vista::Windows Vista/7
+
# Odds, ends, mods:
S52:128:1:48:M1260,N,N,S: Windows:2000:cisco:Windows XP/2000 via Cisco