iptables-1.3.2-20050720
authorMark Huang <mlhuang@cs.princeton.edu>
Mon, 20 Feb 2006 16:22:48 +0000 (16:22 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Mon, 20 Feb 2006 16:22:48 +0000 (16:22 +0000)
269 files changed:
COMMIT_NOTES [new file with mode: 0644]
INCOMPATIBILITIES
INSTALL
Makefile
Rules.make
extensions/.CLUSTERIP-test [new file with mode: 0755]
extensions/.account-test [new file with mode: 0755]
extensions/.childlevel-test [new file with mode: 0755]
extensions/.connrate-test [new file with mode: 0755]
extensions/.dstlimit-test [new file with mode: 0755]
extensions/.set-test [new file with mode: 0755]
extensions/Makefile
extensions/libip6t_HL.c
extensions/libip6t_HL.man [new file with mode: 0644]
extensions/libip6t_LOG.c
extensions/libip6t_LOG.man [new file with mode: 0644]
extensions/libip6t_MARK.c
extensions/libip6t_MARK.man [new file with mode: 0644]
extensions/libip6t_REJECT.c
extensions/libip6t_REJECT.man [new file with mode: 0644]
extensions/libip6t_ROUTE.c
extensions/libip6t_ROUTE.man [new file with mode: 0644]
extensions/libip6t_TRACE.man [new file with mode: 0644]
extensions/libip6t_ah.man [new file with mode: 0644]
extensions/libip6t_condition.c
extensions/libip6t_condition.man [new file with mode: 0644]
extensions/libip6t_dst.man [new file with mode: 0644]
extensions/libip6t_esp.man [new file with mode: 0644]
extensions/libip6t_eui64.c
extensions/libip6t_eui64.man [new file with mode: 0644]
extensions/libip6t_frag.man [new file with mode: 0644]
extensions/libip6t_fuzzy.c
extensions/libip6t_fuzzy.man [new file with mode: 0644]
extensions/libip6t_hbh.c
extensions/libip6t_hbh.man [new file with mode: 0644]
extensions/libip6t_hl.c
extensions/libip6t_hl.man [new file with mode: 0644]
extensions/libip6t_icmpv6.c
extensions/libip6t_icmpv6.man [new file with mode: 0644]
extensions/libip6t_ipv6header.c
extensions/libip6t_ipv6header.man [new file with mode: 0644]
extensions/libip6t_length.c
extensions/libip6t_length.man [new file with mode: 0644]
extensions/libip6t_limit.c
extensions/libip6t_limit.man [new file with mode: 0644]
extensions/libip6t_mac.c
extensions/libip6t_mac.man [new file with mode: 0644]
extensions/libip6t_mark.c
extensions/libip6t_mark.man [new file with mode: 0644]
extensions/libip6t_multiport.c
extensions/libip6t_multiport.man [new file with mode: 0644]
extensions/libip6t_nth.c
extensions/libip6t_nth.man [new file with mode: 0644]
extensions/libip6t_owner.c
extensions/libip6t_owner.man [new file with mode: 0644]
extensions/libip6t_physdev.c [new file with mode: 0644]
extensions/libip6t_physdev.man [new file with mode: 0644]
extensions/libip6t_random.c
extensions/libip6t_random.man [new file with mode: 0644]
extensions/libip6t_rt.c
extensions/libip6t_rt.man [new file with mode: 0644]
extensions/libip6t_standard.c
extensions/libip6t_tcp.c
extensions/libip6t_tcp.man [new file with mode: 0644]
extensions/libip6t_udp.c
extensions/libip6t_udp.man [new file with mode: 0644]
extensions/libipt_BALANCE.c
extensions/libipt_BALANCE.man [new file with mode: 0644]
extensions/libipt_CLASSIFY.c
extensions/libipt_CLASSIFY.man [new file with mode: 0644]
extensions/libipt_CLUSTERIP.c [new file with mode: 0644]
extensions/libipt_CLUSTERIP.man [new file with mode: 0644]
extensions/libipt_CONNMARK.c
extensions/libipt_CONNMARK.man [new file with mode: 0644]
extensions/libipt_DNAT.c
extensions/libipt_DNAT.man [new file with mode: 0644]
extensions/libipt_DSCP.c
extensions/libipt_DSCP.man [new file with mode: 0644]
extensions/libipt_ECN.c
extensions/libipt_ECN.man [new file with mode: 0644]
extensions/libipt_FTOS.c
extensions/libipt_IPMARK.c
extensions/libipt_IPMARK.man [new file with mode: 0644]
extensions/libipt_IPV4OPTSSTRIP.c
extensions/libipt_IPV4OPTSSTRIP.man [new file with mode: 0644]
extensions/libipt_LOG.c
extensions/libipt_LOG.man [new file with mode: 0644]
extensions/libipt_MARK.c
extensions/libipt_MARK.man [new file with mode: 0644]
extensions/libipt_MASQUERADE.c
extensions/libipt_MASQUERADE.man [new file with mode: 0644]
extensions/libipt_MIRROR.c
extensions/libipt_MIRROR.man [new file with mode: 0644]
extensions/libipt_NETLINK.c
extensions/libipt_NETMAP.c
extensions/libipt_NETMAP.man [new file with mode: 0644]
extensions/libipt_NOTRACK.man [new file with mode: 0644]
extensions/libipt_REDIRECT.c
extensions/libipt_REDIRECT.man [new file with mode: 0644]
extensions/libipt_REJECT.c
extensions/libipt_REJECT.man [new file with mode: 0644]
extensions/libipt_ROUTE.c
extensions/libipt_ROUTE.man [new file with mode: 0644]
extensions/libipt_SAME.c
extensions/libipt_SAME.man [new file with mode: 0644]
extensions/libipt_SET.c [new file with mode: 0644]
extensions/libipt_SET.man [new file with mode: 0644]
extensions/libipt_SNAT.c
extensions/libipt_SNAT.man [new file with mode: 0644]
extensions/libipt_TARPIT.c
extensions/libipt_TARPIT.man [new file with mode: 0644]
extensions/libipt_TCPLAG.c
extensions/libipt_TCPMSS.c
extensions/libipt_TCPMSS.man [new file with mode: 0644]
extensions/libipt_TOS.c
extensions/libipt_TOS.man [new file with mode: 0644]
extensions/libipt_TRACE.man [new file with mode: 0644]
extensions/libipt_TTL.c
extensions/libipt_TTL.man [new file with mode: 0644]
extensions/libipt_ULOG.c
extensions/libipt_ULOG.man [new file with mode: 0644]
extensions/libipt_XOR.c
extensions/libipt_XOR.man [new file with mode: 0644]
extensions/libipt_account.c [new file with mode: 0644]
extensions/libipt_account.man [new file with mode: 0644]
extensions/libipt_addrtype.c
extensions/libipt_addrtype.man [new file with mode: 0644]
extensions/libipt_ah.c
extensions/libipt_ah.man [new file with mode: 0644]
extensions/libipt_childlevel.c [new file with mode: 0644]
extensions/libipt_childlevel.man [new file with mode: 0644]
extensions/libipt_comment.c [new file with mode: 0644]
extensions/libipt_comment.man [new file with mode: 0644]
extensions/libipt_condition.c
extensions/libipt_condition.man [new file with mode: 0644]
extensions/libipt_connbytes.c
extensions/libipt_connbytes.man [new file with mode: 0644]
extensions/libipt_connlimit.c
extensions/libipt_connlimit.man [new file with mode: 0644]
extensions/libipt_connmark.c
extensions/libipt_connmark.man [new file with mode: 0644]
extensions/libipt_connrate.c [new file with mode: 0644]
extensions/libipt_connrate.man [new file with mode: 0644]
extensions/libipt_conntrack.c
extensions/libipt_conntrack.man [new file with mode: 0644]
extensions/libipt_dscp.c
extensions/libipt_dscp.man [new file with mode: 0644]
extensions/libipt_dstlimit.c [new file with mode: 0644]
extensions/libipt_dstlimit.man [new file with mode: 0644]
extensions/libipt_ecn.c
extensions/libipt_ecn.man [new file with mode: 0644]
extensions/libipt_esp.c
extensions/libipt_esp.man [new file with mode: 0644]
extensions/libipt_fuzzy.c
extensions/libipt_fuzzy.man [new file with mode: 0644]
extensions/libipt_hashlimit.c [new file with mode: 0644]
extensions/libipt_hashlimit.man [new file with mode: 0644]
extensions/libipt_helper.c
extensions/libipt_helper.man [new file with mode: 0644]
extensions/libipt_icmp.c
extensions/libipt_icmp.man [new file with mode: 0644]
extensions/libipt_iprange.c
extensions/libipt_iprange.man [new file with mode: 0644]
extensions/libipt_ipv4options.c
extensions/libipt_ipv4options.man [new file with mode: 0644]
extensions/libipt_length.c
extensions/libipt_length.man [new file with mode: 0644]
extensions/libipt_limit.c
extensions/libipt_limit.man [new file with mode: 0644]
extensions/libipt_mac.c
extensions/libipt_mac.man [new file with mode: 0644]
extensions/libipt_mark.c
extensions/libipt_mark.man [new file with mode: 0644]
extensions/libipt_mport.c
extensions/libipt_mport.man [new file with mode: 0644]
extensions/libipt_multiport.c
extensions/libipt_multiport.man [new file with mode: 0644]
extensions/libipt_nth.c
extensions/libipt_nth.man [new file with mode: 0644]
extensions/libipt_osf.c
extensions/libipt_osf.man [new file with mode: 0644]
extensions/libipt_owner.c
extensions/libipt_owner.man [new file with mode: 0644]
extensions/libipt_physdev.c
extensions/libipt_physdev.man [new file with mode: 0644]
extensions/libipt_pkttype.c
extensions/libipt_pkttype.man [new file with mode: 0644]
extensions/libipt_psd.c
extensions/libipt_psd.man [new file with mode: 0644]
extensions/libipt_quota.c
extensions/libipt_quota.man [new file with mode: 0644]
extensions/libipt_random.c
extensions/libipt_random.man [new file with mode: 0644]
extensions/libipt_realm.c
extensions/libipt_realm.man [new file with mode: 0644]
extensions/libipt_recent.c
extensions/libipt_recent.man [new file with mode: 0644]
extensions/libipt_record_rpc.c
extensions/libipt_rpc.c
extensions/libipt_sctp.c
extensions/libipt_sctp.man [new file with mode: 0644]
extensions/libipt_set.c [new file with mode: 0644]
extensions/libipt_set.h [new file with mode: 0644]
extensions/libipt_set.man [new file with mode: 0644]
extensions/libipt_standard.c
extensions/libipt_state.c
extensions/libipt_state.man [new file with mode: 0644]
extensions/libipt_string.c
extensions/libipt_tcp.c
extensions/libipt_tcp.man [new file with mode: 0644]
extensions/libipt_tcpmss.c
extensions/libipt_tcpmss.man [new file with mode: 0644]
extensions/libipt_time.c
extensions/libipt_time.man [new file with mode: 0644]
extensions/libipt_tos.c
extensions/libipt_tos.man [new file with mode: 0644]
extensions/libipt_ttl.c
extensions/libipt_ttl.man [new file with mode: 0644]
extensions/libipt_u32.c
extensions/libipt_u32.man [new file with mode: 0644]
extensions/libipt_udp.c
extensions/libipt_udp.man [new file with mode: 0644]
extensions/libipt_unclean.c
extensions/libipt_unclean.man [new file with mode: 0644]
include/ip6tables.h
include/iptables.h
include/iptables_common.h
include/linux/netfilter_ipv4/ipt_CLUSTERIP.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_CONNMARK.h
include/linux/netfilter_ipv4/ipt_MARK.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_SAME.h
include/linux/netfilter_ipv4/ipt_ULOG.h
include/linux/netfilter_ipv4/ipt_addrtype.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_comment.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_connmark.h
include/linux/netfilter_ipv4/ipt_conntrack.h
include/linux/netfilter_ipv4/ipt_dstlimit.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_hashlimit.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_limit.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_mark.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_multiport.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_sctp.h
include/linux/netfilter_ipv6/ip6t_MARK.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_limit.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_mark.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_physdev.h [new file with mode: 0644]
ip6tables-restore.c
ip6tables-save.c
ip6tables-standalone.c
ip6tables.8.in [new file with mode: 0644]
ip6tables.c
iptables-multi.c [new file with mode: 0644]
iptables-restore.c
iptables-save.c
iptables-standalone.c
iptables.8.in [new file with mode: 0644]
iptables.c
libipq/ipq_create_handle.3
libipq/ipq_errstr.3
libipq/ipq_message_type.3
libipq/ipq_read.3
libipq/ipq_set_mode.3
libipq/ipq_set_verdict.3
libipq/libipq.3
libiptc/libip4tc.c
libiptc/libip6tc.c
libiptc/libiptc.c
libiptc/linux_list.h [new file with mode: 0644]
libiptc/linux_stddef.h [new file with mode: 0644]

diff --git a/COMMIT_NOTES b/COMMIT_NOTES
new file mode 100644 (file)
index 0000000..5b6e6f7
--- /dev/null
@@ -0,0 +1,24 @@
+A quick list of rules for committing stuff into netfilter svn:
+
+- Always include the Name of the Author/Contributor in the SVN comment
+  like 'fix for foo (Au Thor)'
+
+- make sure that you have set the executable bits on an 'extensions/.*-test'
+  script before adding/committing it to SVN
+
+- If the commit fixes a bugzilla bug, please include '(Closes: #bugnr)' in
+  the commit message
+
+- Make sure you don't commit to svn while a feature freeze is announced
+
+- For new extensions, there are two possible cases:
+       1) header files are just in patch-o-matic patch, you need an
+          'extensions/.*-test' script to have a conditional build
+       2) header files are in patch-o-matic patch, and copied to
+          'netfilter/include/linux/netfilter_xxx'.  This way the extension
+          can be built _unconditionally_, and thus be included in
+          'extensions/Makefile'.  Make sure to keep the headers in sync!
+
+  Usually '1)' is used, but in case something is expected to show up in the
+  kernel soon, we should already make userspace support unconditionally.
+
index fd695e1..7057b26 100644 (file)
@@ -4,3 +4,9 @@ INCOMPATIBILITIES:
   with kernels that do not support it, will result in a plain DROP instead
   of REJECT.  Use with caution.
   Kernels that do support it:
+
+- There are some issues related to upgrading from 1.2.x to 1.3.x on a system
+  with dynamic ruleset changes during runtime. (Please see 
+  https://bugzilla.netfilter.org/bugzilla/show_bug.cgi?id=334).
+  After upgrading from 1.2 to 1.3, it suggest go do an iptables-save, then
+  iptables-restore to ensure your dynamic rule changes continue to work.
diff --git a/INSTALL b/INSTALL
index 3455fa6..5e840c6 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -25,7 +25,7 @@ PROBLEMS YOU MAY ENCOUNTER:
        Please try `make KERNEL_DIR=path-to-correct-kernel'
 
 3) If you want to specify alternate directories for installation
-(instead of /usr/ bin lib man), do this:
+(instead of /usr/local/ bin lib man), do this:
 
        % make BINDIR=/usr/bin LIBDIR=/usr/lib MANDIR=/usr/man
        # make BINDIR=/usr/bin LIBDIR=/usr/lib MANDIR=/usr/man install
@@ -36,6 +36,12 @@ PROBLEMS YOU MAY ENCOUNTER:
 
        % make NO_SHARED_LIBS=1
 
+5) If you want to build a single BusyBox style multipurpose binary instead of
+   the individual iptables, iptables-save and iptables-restore binaries, then
+   please use
+
+       % make DO_MULTI=1
+
 NOTE: make sure you build with at least the correct LIBDIR=
 specification, otherwise iptables(8) won't know where to find the
 dynamic objects.
index de4e199..cd2fe5f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -14,10 +14,10 @@ TOPLEVEL_INCLUDED=YES
 ifndef KERNEL_DIR
 KERNEL_DIR=/usr/src/linux
 endif
-IPTABLES_VERSION:=1.2.9
-OLD_IPTABLES_VERSION:=1.2.8
+IPTABLES_VERSION:=1.3.2
+OLD_IPTABLES_VERSION:=1.3.1
 
-PREFIX:=/usr
+PREFIX:=/usr/local
 LIBDIR:=$(PREFIX)/lib
 BINDIR:=$(PREFIX)/sbin
 MANDIR:=$(PREFIX)/man
@@ -38,30 +38,17 @@ ifdef NO_SHARED_LIBS
 CFLAGS += -DNO_SHARED_LIBS=1
 endif
 
-ifndef NO_SHARED_LIBS
-DEPFILES = $(SHARED_LIBS:%.so=%.d)
-SH_CFLAGS:=$(CFLAGS) -fPIC
-STATIC_LIBS  =
-STATIC6_LIBS =
-LDFLAGS      = -rdynamic
-LDLIBS       = -ldl
-else
-DEPFILES = $(EXT_OBJS:%.o=%.d)
-STATIC_LIBS  = extensions/libext.a
-STATIC6_LIBS = extensions/libext6.a
-LDFLAGS      = -static
-LDLIBS       =
-endif
-
-EXTRAS+=iptables iptables.o
+EXTRAS+=iptables iptables.o iptables.8
 EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables $(DESTDIR)$(MANDIR)/man8/iptables.8
 
 # No longer experimental.
+ifneq ($(DO_MULTI), 1)
 EXTRAS+=iptables-save iptables-restore
+endif
 EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables-save $(DESTDIR)$(BINDIR)/iptables-restore $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8
 
 ifeq ($(DO_IPV6), 1)
-EXTRAS+=ip6tables ip6tables.o
+EXTRAS+=ip6tables ip6tables.o ip6tables.8
 EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ip6tables $(DESTDIR)$(MANDIR)/man8/ip6tables.8
 EXTRAS_EXP+=ip6tables-save ip6tables-restore
 EXTRA_INSTALLS_EXP+=$(DESTDIR)$(BINDIR)/ip6tables-save $(DESTDIR)$(BINDIR)/ip6tables-restore # $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-restore.8
@@ -69,20 +56,56 @@ endif
 
 # Sparc64 hack
 ifeq ($(shell uname -m),sparc64)
-# The kernel is 64-bit, even though userspace is 32.
-CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+       POINTERTEST:=1
+       32bituser := $(shell echo -e "\#include <stdio.h>\n\#if !defined(__sparcv9) && !defined(__arch64__) && !defined(_LP64)\nuserspace_is_32bit\n\#endif" | $(CC) $(CFLAGS) -E - | grep userspace_is_32bit)
+       ifdef 32bituser
+               # The kernel is 64-bit, even though userspace is 32.
+               CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+       else
+               EXT_LDFLAGS=-m elf64_sparc
+       endif
 endif
 
-# HPPA hack
-ifeq ($(shell uname -m),parisc64)
-# The kernel is 64-bit, even though userspace is 32.
-CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+# Alpha only has 64bit userspace and fails the test below
+ifeq ($(shell uname -m), alpha)
+       POINTERTEST:=1
+endif
+
+# Generic test if arch wasn't found above
+ifneq ($(POINTERTEST),1)
+       # Try to determine if kernel is 64bit and we are compiling for 32bit
+       ifeq ($(shell [ -a $(KERNEL_DIR)/include/asm ] && echo YES), YES)
+               64bitkernel := $(shell echo -e "\#include <asm/types.h>\n\#if BITS_PER_LONG == 64\nkernel_is_64bits\n\#endif" | $(CC) $(CFLAGS) -D__KERNEL__ -E - | grep kernel_is_64bits)
+               ifdef 64bitkernel
+                       32bituser := $(shell echo -e "\#include <stdio.h>\n\#if !defined(__arch64__) && !defined(_LP64)\nuserspace_is_32bit\n\#endif" | $(CC) $(CFLAGS) -E - | grep userspace_is_32bit)
+                       ifdef 32bituser
+                               CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+                       endif
+               endif
+       else
+               CFLAGS+=-D_UNKNOWN_KERNEL_POINTER_SIZE
+       endif
 endif
 
 ifndef IPT_LIBDIR
 IPT_LIBDIR:=$(LIBDIR)/iptables
 endif
 
+ifndef NO_SHARED_LIBS
+DEPFILES = $(SHARED_LIBS:%.so=%.d)
+SH_CFLAGS:=$(CFLAGS) -fPIC
+STATIC_LIBS  =
+STATIC6_LIBS =
+LDFLAGS      = -rdynamic
+LDLIBS       = -ldl -lnsl
+else
+DEPFILES = $(EXT_OBJS:%.o=%.d)
+STATIC_LIBS  = extensions/libext.a
+STATIC6_LIBS = extensions/libext6.a
+LDFLAGS      = -static
+LDLIBS       =
+endif
+
 .PHONY: default
 default: print-extensions all
 
@@ -93,8 +116,13 @@ print-extensions:
 iptables.o: iptables.c
        $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
 
+ifeq ($(DO_MULTI), 1)
+iptables: iptables-multi.c iptables-save.c iptables-restore.c iptables-standalone.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPTABLES_MULTI -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+else
 iptables: iptables-standalone.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
        $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+endif
 
 $(DESTDIR)$(BINDIR)/iptables: iptables
        @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
@@ -103,16 +131,28 @@ $(DESTDIR)$(BINDIR)/iptables: iptables
 iptables-save: iptables-save.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
        $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
 
+ifeq ($(DO_MULTI), 1)
+$(DESTDIR)$(BINDIR)/iptables-save: iptables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       ln -sf $< $@
+else
 $(DESTDIR)$(BINDIR)/iptables-save: iptables-save
        @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
        cp $< $@
+endif
 
 iptables-restore: iptables-restore.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
        $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
 
+ifeq ($(DO_MULTI), 1)
+$(DESTDIR)$(BINDIR)/iptables-restore: iptables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       ln -sf $< $@
+else
 $(DESTDIR)$(BINDIR)/iptables-restore: iptables-restore
        @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
        cp $< $@
+endif
 
 ip6tables.o: ip6tables.c
        $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
@@ -147,6 +187,11 @@ EXTRA_DEPENDS+=iptables-standalone.d iptables.d
 iptables-standalone.d iptables.d: %.d: %.c
        @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.o:@' > $@
 
+iptables.8: iptables.8.in extensions/libipt_matches.man extensions/libipt_targets.man
+       sed -e '/@MATCH@/ r extensions/libipt_matches.man' -e '/@TARGET@/ r extensions/libipt_targets.man' iptables.8.in >iptables.8
+
+ip6tables.8: ip6tables.8.in extensions/libip6t_matches.man extensions/libip6t_targets.man
+       sed -e '/@MATCH@/ r extensions/libip6t_matches.man' -e '/@TARGET@/ r extensions/libip6t_targets.man' ip6tables.8.in >ip6tables.8
 
 # Development Targets
 .PHONY: install-devel-man3
@@ -190,7 +235,7 @@ delrelease:
        rm -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
 
 $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2:
-       cd .. && ln -sf userspace iptables-$(IPTABLES_VERSION) && tar cvf - --exclude CVS iptables-$(IPTABLES_VERSION)/. | bzip2 -9 > $@ && rm iptables-$(IPTABLES_VERSION)
+       cd .. && ln -sf iptables iptables-$(IPTABLES_VERSION) && tar cvf - --exclude .svn iptables-$(IPTABLES_VERSION)/. | bzip2 -9 > $@ && rm iptables-$(IPTABLES_VERSION)
 
 .PHONY: diff
 diff: $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
index e8aa160..c0c5010 100644 (file)
@@ -11,9 +11,9 @@ clean: $(EXTRA_CLEANS)
        @find . -name '*.[ao]' -o -name '*.so' | xargs rm -f
 
 install: all $(EXTRA_INSTALLS)
-       @if [ -f /usr/bin/iptables -a "$(BINDIR)" = "/usr/sbin" ];\
-       then echo 'Erasing iptables from old location (now /usr/sbin).';\
-       rm -f /usr/bin/iptables;\
+       @if [ -f /usr/local/bin/iptables -a "$(BINDIR)" = "/usr/local/sbin" ];\
+       then echo 'Erasing iptables from old location (now /usr/local/sbin).';\
+       rm -f /usr/local/bin/iptables;\
        fi
 
 install-experimental: $(EXTRA_INSTALLS_EXP)
@@ -31,7 +31,7 @@ $(SHARED_LIBS:%.so=%.d): %.d: %.c
            sed -e 's@^.*\.o:@$*.d $*_sh.o:@' > $@
 
 $(SHARED_LIBS): %.so : %_sh.o
-       $(LD) -shared -o $@ $<
+       $(LD) -shared $(EXT_LDFLAGS) -o $@ $<
 
 %_sh.o : %.c
        $(CC) $(SH_CFLAGS) -o $@ -c $<
diff --git a/extensions/.CLUSTERIP-test b/extensions/.CLUSTERIP-test
new file mode 100755 (executable)
index 0000000..6d0017a
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_CLUSTERIP.c ] && echo CLUSTERIP
diff --git a/extensions/.account-test b/extensions/.account-test
new file mode 100755 (executable)
index 0000000..68aeb16
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if account match patch is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_account.h ] && echo account
diff --git a/extensions/.childlevel-test b/extensions/.childlevel-test
new file mode 100755 (executable)
index 0000000..9f3b965
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_childlevel.h ] && echo childlevel
diff --git a/extensions/.connrate-test b/extensions/.connrate-test
new file mode 100755 (executable)
index 0000000..d110c15
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_connrate.h ] && echo connrate
diff --git a/extensions/.dstlimit-test b/extensions/.dstlimit-test
new file mode 100755 (executable)
index 0000000..b7c8ef9
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_dstlimit.c ] && echo dstlimit
diff --git a/extensions/.set-test b/extensions/.set-test
new file mode 100755 (executable)
index 0000000..700a73c
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ip_set.h ] && echo set SET
index db9d604..5840502 100644 (file)
@@ -5,13 +5,36 @@
 # header files are present in the include/linux directory of this iptables
 # package (HW)
 #
-PF_EXT_SLIB:=ah connlimit connmark conntrack dscp ecn esp helper icmp iprange length limit mac mark multiport owner physdev pkttype realm rpc standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NOTRACK REDIRECT REJECT SAME SNAT TARPIT TCPMSS TOS TRACE TTL ULOG
-PF6_EXT_SLIB:=eui64 hl icmpv6 length limit mac mark multiport owner standard tcp udp HL LOG MARK TRACE
+PF_EXT_SLIB:=ah addrtype comment connlimit connmark conntrack dscp ecn esp hashlimit helper icmp iprange length limit mac mark multiport owner physdev pkttype realm rpc sctp standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NOTRACK REDIRECT REJECT SAME SNAT TARPIT TCPMSS TOS TRACE TTL ULOG
+PF6_EXT_SLIB:=eui64 hl icmpv6 length limit mac mark multiport owner physdev standard tcp udp HL LOG MARK TRACE
 
 # Optionals
 PF_EXT_SLIB_OPTS:=$(foreach T,$(wildcard extensions/.*-test),$(shell KERNEL_DIR=$(KERNEL_DIR) $(T)))
 PF6_EXT_SLIB_OPTS:=$(foreach T,$(wildcard extensions/.*-test6),$(shell KERNEL_DIR=$(KERNEL_DIR) $(T)))
 
+PF_EXT_ALL_SLIB:=$(patsubst extensions/libipt_%.c, %, $(wildcard extensions/libipt_*.c))
+PF6_EXT_ALL_SLIB:=$(patsubst extensions/libip6t_%.c, %, $(wildcard extensions/libip6t_*.c))
+
+PF_EXT_MAN_ALL_MATCHES:=$(foreach T,$(PF_EXT_ALL_SLIB),$(shell test -f extensions/libipt_$(T).man && grep -q register_match extensions/libipt_$(T).c  && echo $(T)))
+PF_EXT_MAN_ALL_TARGETS:=$(foreach T,$(PF_EXT_ALL_SLIB),$(shell test -f extensions/libipt_$(T).man && grep -q register_target extensions/libipt_$(T).c && echo $(T)))
+PF6_EXT_MAN_ALL_MATCHES:=$(foreach T,$(PF6_EXT_ALL_SLIB),$(shell test -f extensions/libip6t_$(T).man && grep -q register_match6 extensions/libip6t_$(T).c  && echo $(T)))
+PF6_EXT_MAN_ALL_TARGETS:=$(foreach T,$(PF6_EXT_ALL_SLIB),$(shell test -f extensions/libip6t_$(T).man && grep -q register_target6 extensions/libip6t_$(T).c && echo $(T)))
+
+PF_EXT_MAN_MATCHES:=$(filter $(PF_EXT_ALL_SLIB), $(PF_EXT_MAN_ALL_MATCHES))
+PF_EXT_MAN_TARGETS:=$(filter $(PF_EXT_ALL_SLIB), $(PF_EXT_MAN_ALL_TARGETS))
+PF_EXT_MAN_EXTRA_MATCHES:=$(filter-out $(PF_EXT_MAN_MATCHES), $(PF_EXT_MAN_ALL_MATCHES))
+PF_EXT_MAN_EXTRA_TARGETS:=$(filter-out $(PF_EXT_MAN_TARGETS), $(PF_EXT_MAN_ALL_TARGETS))
+PF6_EXT_MAN_MATCHES:=$(filter $(PF6_EXT_ALL_SLIB), $(PF6_EXT_MAN_ALL_MATCHES))
+PF6_EXT_MAN_TARGETS:=$(filter $(PF6_EXT_ALL_SLIB), $(PF6_EXT_MAN_ALL_TARGETS))
+PF6_EXT_MAN_EXTRA_MATCHES:=$(filter-out $(PF6_EXT_MAN_MATCHES), $(PF6_EXT_MAN_ALL_MATCHES))
+PF6_EXT_MAN_EXTRA_TARGETS:=$(filter-out $(PF6_EXT_MAN_TARGETS), $(PF6_EXT_MAN_ALL_TARGETS))
+
+
+allman:
+       @echo ALL_SLIB: $(PF_EXT_ALL_SLIB)
+       @echo ALL_MATCH: $(PF_EXT_MAN_ALL_MATCHES)
+       @echo ALL_TARGET: $(PF_EXT_MAN_ALL_TARGETS)
+
 PF_EXT_SLIB+=$(PF_EXT_SLIB_OPTS)
 PF6_EXT_SLIB+=$(PF6_EXT_SLIB_OPTS)
 
@@ -79,6 +102,62 @@ extensions/lib%.o: extensions/lib%.c
 
 endif
  
+EXTRAS += extensions/libipt_targets.man
+extensions/libipt_targets.man: $(patsubst %,extensions/libipt_%.man,$(PF_EXT_MAN_ALL_TARGETS))
+       @for ext in $(PF_EXT_MAN_TARGETS); do \
+           echo ".SS $$ext" ;\
+           cat extensions/libipt_$$ext.man ;\
+       done >extensions/libipt_targets.man
+       @if [ -n "$(PF_EXT_MAN_EXTRA_TARGETS)" ]; then \
+           extra=$(PF_EXT_MAN_EXTRA_TARGETS) ;\
+           for ext in $${extra:-""}; do \
+               echo ".SS $$ext (not supported, see Patch-O-Matic)" ;\
+               cat extensions/libipt_$$ext.man ;\
+           done ;\
+               fi >>extensions/libipt_targets.man
+
+EXTRAS += extensions/libipt_matches.man
+extensions/libipt_matches.man: $(patsubst %,extensions/libipt_%.man,$(PF_EXT_MAN_ALL_MATCHES))
+       @for ext in $(PF_EXT_MAN_MATCHES); do \
+           echo ".SS $$ext" ;\
+           cat extensions/libipt_$$ext.man ;\
+       done >extensions/libipt_matches.man
+       @if [ -n "$(PF_EXT_MAN_EXTRA_MATCHES)" ]; then \
+           extra=$(PF_EXT_MAN_EXTRA_MATCHES) ;\
+           for ext in $${extra:-""}; do \
+               echo ".SS $$ext (not supported, see Patch-O-Matic)" ;\
+               cat extensions/libipt_$$ext.man ;\
+           done ;\
+               fi >>extensions/libipt_matches.man
+
+EXTRAS += extensions/libip6t_targets.man
+extensions/libip6t_targets.man: $(patsubst %, extensions/libip6t_%.man, $(PF6_EXT_MAN_ALL_TARGETS))
+       @for ext in $(PF6_EXT_MAN_TARGETS); do \
+           echo ".SS $$ext" ;\
+           cat extensions/libip6t_$$ext.man ;\
+       done >extensions/libip6t_targets.man
+       @if [ -n "$(PF6_EXT_MAN_EXTRA_TARGETS)" ]; then \
+           extra=$(PF6_EXT_MAN_EXTRA_TARGETS) ;\
+           for ext in $${extra:-""}; do \
+               echo ".SS $$ext (not supported, see Patch-O-Matic)" ;\
+               cat extensions/libip6t_$$ext.man ;\
+           done ;\
+               fi >>extensions/libip6t_targets.man
+
+EXTRAS += extensions/libip6t_matches.man
+extensions/libip6t_matches.man: $(patsubst %, extensions/libip6t_%.man, $(PF6_EXT_MAN_ALL_MATCHES))
+       @for ext in $(PF6_EXT_MAN_MATCHES); do \
+           echo ".SS $$ext" ;\
+           cat extensions/libip6t_$$ext.man ;\
+       done >extensions/libip6t_matches.man
+       @if [ -n "$(PF6_EXT_MAN_EXTRA_MATCHES)" ]; then \
+           extra=$(PF6_EXT_MAN_EXTRA_MATCHES) ;\
+           for ext in $${extra:-""}; do \
+               echo ".SS $$ext (not supported, see Patch-O-Matic)" ;\
+               cat extensions/libip6t_$$ext.man ;\
+           done ;\
+               fi >>extensions/libip6t_matches.man
+
 $(DESTDIR)$(LIBDIR)/iptables/libipt_%.so: extensions/libipt_%.so
        @[ -d $(DESTDIR)$(LIBDIR)/iptables ] || mkdir -p $(DESTDIR)$(LIBDIR)/iptables
        cp $< $@
index 7c24915..2062828 100644 (file)
@@ -24,9 +24,9 @@ static void help(void)
 {
        printf(
 "HL target v%s options\n"
-"  --hl-set value              Set HL to <value>\n"
-"  --hl-dec value              Decrement HL by <value>\n"
-"  --hl-inc value              Increment HL by <value>\n"
+"  --hl-set value              Set HL to <value 0-255>\n"
+"  --hl-dec value              Decrement HL by <value 1-255>\n"
+"  --hl-inc value              Increment HL by <value 1-255>\n"
 , IPTABLES_VERSION);
 }
 
@@ -35,7 +35,7 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
                struct ip6t_entry_target **target)
 {
        struct ip6t_HL_info *info = (struct ip6t_HL_info *) (*target)->data;
-       u_int8_t value;
+       unsigned int value;
 
        if (*flags & IP6T_HL_USED) {
                exit_error(PARAMETER_PROBLEM, 
@@ -50,7 +50,9 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
                exit_error(PARAMETER_PROBLEM,
                                "HL: unexpected `!'");
        
-       value = atoi(optarg);
+       if (string_to_number(optarg, 0, 255, &value) == -1)     
+               exit_error(PARAMETER_PROBLEM,   
+                          "HL: Expected value between 0 and 255");
 
        switch (c) {
 
@@ -145,17 +147,17 @@ static struct option opts[] = {
 
 static
 struct ip6tables_target HL = { NULL, 
-       "HL",
-       IPTABLES_VERSION,
-       IP6T_ALIGN(sizeof(struct ip6t_HL_info)),
-       IP6T_ALIGN(sizeof(struct ip6t_HL_info)),
-       &help,
-       &init,
-       &parse,
-       &final_check,
-       &print,
-       &save,
-       opts 
+       .name           = "HL",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_HL_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_HL_info)),
+       .help           = &help, 
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts 
 };
 
 void _init(void)
diff --git a/extensions/libip6t_HL.man b/extensions/libip6t_HL.man
new file mode 100644 (file)
index 0000000..6b8291d
--- /dev/null
@@ -0,0 +1,17 @@
+This is used to modify the IPv6 HOPLIMIT header field.  The HOPLIMIT field is 
+similar to what is known as TTL value in IPv4.  Setting or incrementing the
+HOPLIMIT field can potentially be very dangerous, so it should be avoided at
+any cost.  
+.TP
+.B Don't ever set or increment the value on packets that leave your local network!
+.B mangle
+table.
+.TP
+.BI "--hl-set " "value"
+Set the HOPLIMIT value to `value'.
+.TP
+.BI "--hl-dec " "value"
+Decrement the HOPLIMIT value `value' times.
+.TP
+.BI "--hl-inc " "value"
+Increment the HOPLIMIT value `value' times.
index 997e439..a9c8965 100644 (file)
@@ -9,6 +9,12 @@
 #include <linux/netfilter_ipv6/ip6_tables.h>
 #include <linux/netfilter_ipv6/ip6t_LOG.h>
 
+#ifndef IP6T_LOG_UID   /* Old kernel */
+#define IP6T_LOG_UID   0x08
+#undef  IP6T_LOG_MASK
+#define IP6T_LOG_MASK  0x0f
+#endif
+
 #define LOG_DEFAULT_LEVEL LOG_WARNING
 
 /* Function which prints out usage message. */
@@ -21,7 +27,8 @@ help(void)
 " --log-prefix prefix          Prefix log messages with this prefix.\n\n"
 " --log-tcp-sequence           Log TCP sequence numbers.\n\n"
 " --log-tcp-options            Log TCP options.\n\n"
-" --log-ip-options             Log IP options.\n\n",
+" --log-ip-options             Log IP options.\n\n"
+" --log-uid                    Log UID owning the local socket.\n\n",
 IPTABLES_VERSION);
 }
 
@@ -31,6 +38,7 @@ static struct option opts[] = {
        { .name = "log-tcp-sequence", .has_arg = 0, .flag = 0, .val = '1' },
        { .name = "log-tcp-options",  .has_arg = 0, .flag = 0, .val = '2' },
        { .name = "log-ip-options",   .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = "log-uid",          .has_arg = 0, .flag = 0, .val = '4' },
        { .name = 0 }
 };
 
@@ -42,8 +50,6 @@ init(struct ip6t_entry_target *t, unsigned int *nfcache)
 
        loginfo->level = LOG_DEFAULT_LEVEL;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 struct ip6t_log_names {
@@ -98,6 +104,7 @@ parse_level(const char *level)
 #define IP6T_LOG_OPT_TCPSEQ 0x04
 #define IP6T_LOG_OPT_TCPOPT 0x08
 #define IP6T_LOG_OPT_IPOPT 0x10
+#define IP6T_LOG_OPT_UID 0x20
 
 /* Function which parses command options; returns true if it
    ate an option */
@@ -134,7 +141,11 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
                        exit_error(PARAMETER_PROBLEM,
                                   "Maximum prefix length %u for --log-prefix",
-                                  sizeof(loginfo->prefix) - 1);
+                                  (unsigned int)sizeof(loginfo->prefix) - 1);
+
+               if (strlen(optarg) != strlen(strtok(optarg, "\n")))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Newlines not allowed in --log-prefix");
 
                strcpy(loginfo->prefix, optarg);
                *flags |= IP6T_LOG_OPT_PREFIX;
@@ -168,6 +179,15 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                *flags |= IP6T_LOG_OPT_IPOPT;
                break;
 
+       case '4':
+               if (*flags & IP6T_LOG_OPT_UID)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-uid twice");
+
+               loginfo->logflags |= IP6T_LOG_UID;
+               *flags |= IP6T_LOG_OPT_UID;
+               break;
+
        default:
                return 0;
        }
@@ -211,6 +231,8 @@ print(const struct ip6t_ip6 *ip,
                        printf("tcp-options ");
                if (loginfo->logflags & IP6T_LOG_IPOPT)
                        printf("ip-options ");
+               if (loginfo->logflags & IP6T_LOG_UID)
+                       printf("uid ");
                if (loginfo->logflags & ~(IP6T_LOG_MASK))
                        printf("unknown-flags ");
        }
@@ -238,6 +260,8 @@ save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
                printf("--log-tcp-options ");
        if (loginfo->logflags & IP6T_LOG_IPOPT)
                printf("--log-ip-options ");
+       if (loginfo->logflags & IP6T_LOG_UID)
+               printf("--log-uid ");
 }
 
 static
diff --git a/extensions/libip6t_LOG.man b/extensions/libip6t_LOG.man
new file mode 100644 (file)
index 0000000..9d51fd4
--- /dev/null
@@ -0,0 +1,31 @@
+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 IPv6 IPv6-header fields) via the kernel log
+(where it can be read with
+.I dmesg
+or 
+.IR syslogd (8)).
+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
+then DROP (or REJECT).
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 29 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IPv6 packet header.
+.TP
+.B --log-uid
+Log the userid of the process which generated the packet.
index 292f957..a7f1a9d 100644 (file)
@@ -6,7 +6,8 @@
 
 #include <ip6tables.h>
 #include <linux/netfilter_ipv6/ip6_tables.h>
-#include <linux/netfilter_ipv6/ip6t_MARK.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv6/ip6t_MARK.h"
 
 /* Function which prints out usage message. */
 static void
@@ -41,10 +42,14 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                = (struct ip6t_mark_target_info *)(*target)->data;
 
        switch (c) {
-               char *end;
        case '1':
-               markinfo->mark = strtoul(optarg, &end, 0);
-               if (*end != '\0' || end == optarg)
+#ifdef KERNEL_64_USERSPACE_32
+               if (string_to_number_ll(optarg, 0, 0, 
+                                    &markinfo->mark))
+#else
+               if (string_to_number_l(optarg, 0, 0, 
+                                    &markinfo->mark))
+#endif
                        exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
                if (*flags)
                        exit_error(PARAMETER_PROBLEM,
@@ -67,6 +72,20 @@ final_check(unsigned int flags)
                           "MARK target: Parameter --set-mark is required");
 }
 
+#ifdef KERNEL_64_USERSPACE_32
+static void
+print_mark(unsigned long long mark)
+{
+       printf("0x%llx ", mark);
+}
+#else
+static void
+print_mark(unsigned long mark)
+{
+       printf("0x%lx ", mark);
+}
+#endif
+
 /* Prints out the targinfo. */
 static void
 print(const struct ip6t_ip6 *ip,
@@ -76,7 +95,8 @@ print(const struct ip6t_ip6 *ip,
        const struct ip6t_mark_target_info *markinfo =
                (const struct ip6t_mark_target_info *)target->data;
 
-       printf("MARK set 0x%lx ", markinfo->mark);
+       printf("MARK set ");
+       print_mark(markinfo->mark);
 }
 
 /* Saves the union ipt_targinfo in parsable form to stdout. */
@@ -86,7 +106,8 @@ save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
        const struct ip6t_mark_target_info *markinfo =
                (const struct ip6t_mark_target_info *)target->data;
 
-       printf("--set-mark 0x%lx ", markinfo->mark);
+       printf("--set-mark ");
+       print_mark(markinfo->mark);
 }
 
 static
diff --git a/extensions/libip6t_MARK.man b/extensions/libip6t_MARK.man
new file mode 100644 (file)
index 0000000..1f3260c
--- /dev/null
@@ -0,0 +1,6 @@
+This is used to set the netfilter mark value associated with the
+packet.  It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-mark " "mark"
index 8c4cc7f..879716b 100644 (file)
@@ -79,8 +79,6 @@ init(struct ip6t_entry_target *t, unsigned int *nfcache)
        /* default */
        reject->with = IP6T_ICMP6_PORT_UNREACH;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Function which parses command options; returns true if it
@@ -152,19 +150,18 @@ static void save(const struct ip6t_ip6 *ip,
        printf("--reject-with %s ", reject_table[i].name);
 }
 
-struct ip6tables_target reject
-= { NULL,
-    "REJECT",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_reject_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_reject_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct ip6tables_target reject = {
+       .name = "REJECT",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_reject_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_reject_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_REJECT.man b/extensions/libip6t_REJECT.man
new file mode 100644 (file)
index 0000000..75930f1
--- /dev/null
@@ -0,0 +1,34 @@
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to 
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  The following option controls the nature of the error packet
+returned:
+.TP
+.BI "--reject-with " "type"
+The type given can be
+.nf
+.B " icmp6-no-route"
+.B " no-route"
+.B " icmp6-adm-prohibited"
+.B " adm-prohibited"
+.B " icmp6-addr-unreachable"
+.B " addr-unreach"
+.B " icmp6-port-unreachable"
+.B " port-unreach"
+.fi
+which return the appropriate IPv6-ICMP error message (\fBport-unreach\fP is
+the default). Finally, the option
+.B tcp-reset
+can be used on rules which only match the TCP protocol: this causes a
+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).
+
index 71dada8..ad83a1d 100644 (file)
@@ -1,6 +1,6 @@
 /* Shared library add-on to iptables to add ROUTE v6 target support.
  * Author : Cedric de Launois, <delaunois@info.ucl.ac.be>
- * v 1.0 2003/06/24
+ * v 1.1 2004/11/23
  */
 
 #include <stdio.h>
 #include <linux/netfilter_ipv6/ip6_tables.h>
 #include <linux/netfilter_ipv6/ip6t_ROUTE.h>
 
+/* compile IP6T_ROUTE_TEE support even if kernel headers are unpatched */
+#ifndef IP6T_ROUTE_TEE
+#define IP6T_ROUTE_TEE         0x02
+#endif
+
 /* Function which prints out usage message. */
 static void
 help(void)
@@ -23,9 +28,13 @@ help(void)
 "ROUTE target v%s options:\n"
 "    --oif   \tifname \t\tRoute the packet through `ifname' network interface\n"
 "    --gw    \tip     \t\tRoute the packet via this gateway\n"
-"    --continue\t     \t\tRoute the packet and continue traversing the rules.\n"
+"    --continue\t     \t\tRoute packet and continue traversing the\n"
+"            \t       \t\trules. Not valid with --iif or --tee.\n"
+"    --tee\t  \t\tDuplicate packet, route the duplicate,\n"
+"            \t       \t\tcontinue traversing with original packet.\n"
+"            \t       \t\tNot valid with --iif or --continue.\n"
 "\n",
-"1.0");
+"1.1");
 }
 
 static struct option opts[] = {
@@ -33,6 +42,7 @@ static struct option opts[] = {
        { "iif", 1, 0, '2' },
        { "gw", 1, 0, '3' },
        { "continue", 0, 0, '4' },
+       { "tee", 0, 0, '5' },
        { 0 }
 };
 
@@ -57,6 +67,7 @@ init(struct ip6t_entry_target *t, unsigned int *nfcache)
 #define IP6T_ROUTE_OPT_IIF      0x02
 #define IP6T_ROUTE_OPT_GW       0x04
 #define IP6T_ROUTE_OPT_CONTINUE 0x08
+#define IP6T_ROUTE_OPT_TEE      0x10
 
 /* Function which parses command options; returns true if it
    ate an option */
@@ -114,12 +125,28 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (*flags & IP6T_ROUTE_OPT_CONTINUE)
                        exit_error(PARAMETER_PROBLEM,
                                   "Can't specify --continue twice");
+               if (*flags & IP6T_ROUTE_OPT_TEE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --continue AND --tee");
 
                route_info->flags |= IP6T_ROUTE_CONTINUE;
                *flags |= IP6T_ROUTE_OPT_CONTINUE;
 
                break;
 
+       case '5':
+               if (*flags & IP6T_ROUTE_OPT_TEE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --tee twice");
+               if (*flags & IP6T_ROUTE_OPT_CONTINUE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --tee AND --continue");
+
+               route_info->flags |= IP6T_ROUTE_TEE;
+               *flags |= IP6T_ROUTE_OPT_TEE;
+
+               break;
+
        default:
                return 0;
        }
@@ -162,6 +189,9 @@ print(const struct ip6t_ip6 *ip,
        if (route_info->flags & IP6T_ROUTE_CONTINUE)
                printf("continue");
 
+       if (route_info->flags & IP6T_ROUTE_TEE)
+               printf("tee");
+
 }
 
 
@@ -184,23 +214,24 @@ static void save(const struct ip6t_ip6 *ip,
 
        if (route_info->flags & IP6T_ROUTE_CONTINUE)
                printf("--continue ");
+
+       if (route_info->flags & IP6T_ROUTE_TEE)
+               printf("--tee ");
 }
 
 
-static
-struct ip6tables_target route
-= { NULL,
-    "ROUTE",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_route_target_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_route_target_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_target route = { 
+       .name           = "ROUTE",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_route_target_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_route_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_ROUTE.man b/extensions/libip6t_ROUTE.man
new file mode 100644 (file)
index 0000000..e3ad12b
--- /dev/null
@@ -0,0 +1,15 @@
+This is used to explicitly override the core network stack's routing decision.
+.B mangle
+table.
+.TP
+.BI "--oif " "ifname"
+Route the packet through `ifname' network interface
+.TP
+.BI "--gw " "IPv6_address"
+Route the packet via this gateway
+.TP
+.BI "--continue "
+Behave like a non-terminating target and continue traversing the rules. Not valid in combination with `--tee'
+.TP
+.BI "--tee "
+Make a copy of the packet, and route that copy to the given destination. For the original, uncopied packet, behave like a non-terminating target and continue traversing the rules.  Not valid in combination with `--continue'
diff --git a/extensions/libip6t_TRACE.man b/extensions/libip6t_TRACE.man
new file mode 100644 (file)
index 0000000..549ab33
--- /dev/null
@@ -0,0 +1,3 @@
+This target has no options.  It just turns on 
+.B packet tracing
+for all packets that match this rule.
diff --git a/extensions/libip6t_ah.man b/extensions/libip6t_ah.man
new file mode 100644 (file)
index 0000000..97de1e1
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the SPIs in AH header of IPSec packets.
+.TP
+.BR "--ahspi " "[!] \fIspi\fP[:\fIspi\fP]"
index f58b3bc..0e94c39 100644 (file)
@@ -24,14 +24,6 @@ static struct option opts[] = {
        { .name = 0 }
 };
 
-
-static void
-init(struct ip6t_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
-
 static int
 parse(int c, char **argv, int invert, unsigned int *flags,
       const struct ip6t_entry *entry, unsigned int *nfcache,
@@ -99,7 +91,6 @@ static struct ip6tables_match condition = {
        .size = IP6T_ALIGN(sizeof(struct condition6_info)),
        .userspacesize = IP6T_ALIGN(sizeof(struct condition6_info)),
        .help = &help,
-       .init = &init,
        .parse = &parse,
        .final_check = &final_check,
        .print = &print,
diff --git a/extensions/libip6t_condition.man b/extensions/libip6t_condition.man
new file mode 100644 (file)
index 0000000..30c478c
--- /dev/null
@@ -0,0 +1,4 @@
+This matches if a specific /proc filename is '0' or '1'.
+.TP
+.BI "--condition " "[!] filename"
+Match on boolean value stored in /proc/net/ip6t_condition/filename file
diff --git a/extensions/libip6t_dst.man b/extensions/libip6t_dst.man
new file mode 100644 (file)
index 0000000..168a10f
--- /dev/null
@@ -0,0 +1,7 @@
+This module matches the IPv6 destination header options
+.TP
+.BI "--dst-len" "[!]" "length"
+Total length of this header
+.TP
+.BI "--dst-opts " "TYPE[:LEN],[,TYPE[:LEN]...]"
+Options and it's length (List).
diff --git a/extensions/libip6t_esp.man b/extensions/libip6t_esp.man
new file mode 100644 (file)
index 0000000..7b84368
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the SPIs in ESP header of IPSec packets.
+.TP
+.BR "--espspi " "[!] \fIspi\fP[:\fIspi\fP]"
index 56dca06..c74b04d 100644 (file)
@@ -26,14 +26,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ip6t_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -65,20 +57,17 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
 
 }
 
-static
-struct ip6tables_match eui64
-= { NULL,
-    "eui64",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(int)),
-    IP6T_ALIGN(sizeof(int)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match eui64 = {
+       .name           = "eui64",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(int)),
+       .userspacesize  = IP6T_ALIGN(sizeof(int)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_eui64.man b/extensions/libip6t_eui64.man
new file mode 100644 (file)
index 0000000..24fc56c
--- /dev/null
@@ -0,0 +1 @@
+This module matches the EUI64 part of a stateless autoconfigured IPv6 address.  It compares the source MAC address with the lower 64 bits of the IPv6 address. 
diff --git a/extensions/libip6t_frag.man b/extensions/libip6t_frag.man
new file mode 100644 (file)
index 0000000..fff3db3
--- /dev/null
@@ -0,0 +1,19 @@
+This module matches the time IPv6 fragmentathion header
+.TP
+.BI "--fragid " "[!]" "id[:id]"
+Matches the given fragmentation ID (range).
+.TP
+.BI "--fraglen " "[!]" "length"
+Matches the total length of this header.
+.TP
+.BI "--fragres "
+Matches the reserved field, too.
+.TP
+.BI "--fragfirst "
+Matches on the first fragment.
+.TP
+.BI "[--fragmore]"
+Matches if there are more fragments.
+.TP
+.BI "[--fraglast]"
+Matches if this is the last fragement.
index 65c2acf..749ddc8 100644 (file)
@@ -44,8 +44,6 @@ static void
 init(struct ip6t_entry_match *m, unsigned int *nfcache)
 {
        struct ip6t_fuzzy_info *presentinfo = (struct ip6t_fuzzy_info *)(m)->data;
-       *nfcache |= NFC_UNKNOWN;
-
        /*
         * Default rates ( I'll improve this very soon with something based
         * on real statistics of the running machine ) .
diff --git a/extensions/libip6t_fuzzy.man b/extensions/libip6t_fuzzy.man
new file mode 100644 (file)
index 0000000..270c8d6
--- /dev/null
@@ -0,0 +1,7 @@
+This module matches a rate limit based on a fuzzy logic controller [FLC]
+.TP
+.BI "--lower-limit  "number"
+Specifies the lower limit (in packets per second).
+.TP
+.BI "--upper-limit " "number"
+Specifies the upper limit (in packets per second).
index d0acd42..bdcbf9b 100644 (file)
@@ -237,24 +237,22 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
 
 }
 
-static
-struct ip6tables_match optstruct
-= { NULL,
+static struct ip6tables_match optstruct = {
 #if HOPBYHOP
-    "hbh",
+       .name           = "hbh",
 #else
-    "dst",
+       .name           = "dst",
 #endif
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_opts)),
-    IP6T_ALIGN(sizeof(struct ip6t_opts)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_opts)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_opts)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void
diff --git a/extensions/libip6t_hbh.man b/extensions/libip6t_hbh.man
new file mode 100644 (file)
index 0000000..8376f91
--- /dev/null
@@ -0,0 +1,7 @@
+This module matches the IPv6 hop-by-hop header options
+.TP
+.BI "--hbh-len" "[!]" "length"
+Total length of this header
+.TP
+.BI "--hbh-opts " "TYPE[:LEN],[,TYPE[:LEN]...]"
+Options and it's length (List).
index 2d068b8..208da33 100644 (file)
@@ -25,12 +25,6 @@ static void help(void)
 , IPTABLES_VERSION);
 }
 
-static void init(struct ip6t_entry_match *m, unsigned int *nfcache)
-{
-       /* caching not yet implemented */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static int parse(int c, char **argv, int invert, unsigned int *flags,
                const struct ip6t_entry *entry, unsigned int *nfcache,
                struct ip6t_entry_match **match)
@@ -141,7 +135,6 @@ struct ip6tables_match hl = {
        .size          = IP6T_ALIGN(sizeof(struct ip6t_hl_info)),
        .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_hl_info)),
        .help          = &help,
-       .init          = &init,
        .parse         = &parse,
        .final_check   = &final_check,
        .print         = &print,
diff --git a/extensions/libip6t_hl.man b/extensions/libip6t_hl.man
new file mode 100644 (file)
index 0000000..9fcb730
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches the HOPLIMIT field in the IPv6 header.
+.TP
+.BI "--hl-eq " "value"
+Matches if HOPLIMIT equals the given value.
+.TP
+.BI "--hl-lt " "ttl"
+Matches if HOPLIMIT is less than the given value.
+.TP
+.BI "--hl-gt " "ttl"
+Matches if HOPLIMIT is greater than the given value.
index 52d7871..a29bb38 100644 (file)
@@ -90,7 +90,7 @@ static struct option opts[] = {
        {0}
 };
 
-static unsigned int
+static void
 parse_icmpv6(const char *icmpv6type, u_int8_t *type, u_int8_t code[])
 {
        unsigned int limit = sizeof(icmpv6_codes)/sizeof(struct icmpv6_names);
@@ -141,10 +141,6 @@ parse_icmpv6(const char *icmpv6type, u_int8_t *type, u_int8_t code[])
                        code[1] = 0xFF;
                }
        }
-
-       if (code[0] == 0 && code[1] == 0xFF)
-               return NFC_IP6_SRC_PT;
-       else return NFC_IP6_SRC_PT | NFC_IP6_DST_PT;
 }
 
 /* Initialize the match. */
@@ -169,9 +165,8 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        switch (c) {
        case '1':
                check_inverse(optarg, &invert, &optind, 0);
-               *nfcache |= parse_icmpv6(argv[optind-1],
-                                      &icmpv6info->type,
-                                      icmpv6info->code);
+               parse_icmpv6(argv[optind-1], &icmpv6info->type, 
+                            icmpv6info->code);
                if (invert)
                        icmpv6info->invflags |= IP6T_ICMP_INV;
                break;
@@ -257,19 +252,18 @@ static void final_check(unsigned int flags)
 {
 }
 
-static struct ip6tables_match icmpv6
-= { NULL,
-    "icmp6",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_icmp)),
-    IP6T_ALIGN(sizeof(struct ip6t_icmp)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match icmpv6 = {
+       .name           = "icmp6",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_icmp)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_icmp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_icmpv6.man b/extensions/libip6t_icmpv6.man
new file mode 100644 (file)
index 0000000..2702954
--- /dev/null
@@ -0,0 +1,9 @@
+This extension is loaded if `--protocol ipv6-icmp' or `--protocol icmpv6' is
+specified. It provides the following option:
+.TP
+.BR "--icmpv6-type " "[!] \fItypename\fP"
+This allows specification of the ICMP type, which can be a numeric
+IPv6-ICMP type, or one of the IPv6-ICMP type names shown by the command
+.nf
+ ip6tables -p ipv6-icmp -h
+.fi
index 3437e22..a260e6e 100644 (file)
@@ -162,8 +162,6 @@ init(struct ip6t_entry_match *m, unsigned int *nfcache)
        info->matchflags = 0x00;
        info->invflags = 0x00;
        info->modeflag = 0x00;
-       /* No caching (yet) */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 static unsigned int
@@ -298,19 +296,18 @@ save(const struct ip6t_ip6 *ip,
 }
 
 static
-struct ip6tables_match ipv6header
-= { NULL,
-    "ipv6header",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct ip6tables_match ipv6header = {
+       .name           = "ipv6header",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_ipv6header.man b/extensions/libip6t_ipv6header.man
new file mode 100644 (file)
index 0000000..bec3e18
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches on IPv6 option headers
+.TP
+.BI "--header " "[!]" "headers"
+Matches the given type of headers.  
+Names: hop,dst,route,frag,auth,esp,none,proto
+Long Names: hop-by-hop,ipv6-opts,ipv6-route,ipv6-frag,ah,esp,ipv6-nonxt,protocol
+Numbers: 0,60,43,44,51,50,59
+.TP
+.BI "--soft"
+The header CONTAINS the specified extensions.
index b8fa15b..c944c65 100644 (file)
@@ -26,13 +26,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ip6t_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static u_int16_t
 parse_length(const char *s)
 {
@@ -140,19 +133,17 @@ save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
        print_length((struct ip6t_length_info *)match->data);
 }
 
-struct ip6tables_match length
-= { NULL,
-    "length",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_length_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_length_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct ip6tables_match length = {
+       .name           = "length",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_length_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_length_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_length.man b/extensions/libip6t_length.man
new file mode 100644 (file)
index 0000000..72a6b5d
--- /dev/null
@@ -0,0 +1,4 @@
+This module matches the length of a packet against a specific value
+or range of values.
+.TP
+.BR "--length " "\fIlength\fP[:\fIlength\fP]"
index 9516252..6c88ee1 100644 (file)
@@ -11,7 +11,8 @@
 #include <ip6tables.h>
 #include <stddef.h>
 #include <linux/netfilter_ipv6/ip6_tables.h>
-#include <linux/netfilter_ipv6/ip6t_limit.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv6/ip6t_limit.h"
 
 #define IP6T_LIMIT_AVG "3/hour"
 #define IP6T_LIMIT_BURST       5
@@ -80,8 +81,6 @@ init(struct ip6t_entry_match *m, unsigned int *nfcache)
        parse_rate(IP6T_LIMIT_AVG, &r->avg);
        r->burst = IP6T_LIMIT_BURST;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* FIXME: handle overflow:
@@ -103,19 +102,14 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch(c) {
        case '%':
-               if (check_inverse(optarg, &invert, NULL, 0))
-                       exit_error(PARAMETER_PROBLEM,
-                                  "Unexpected `!' after --limit");
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
                if (!parse_rate(optarg, &r->avg))
                        exit_error(PARAMETER_PROBLEM,
                                   "bad rate `%s'", optarg);
                break;
 
        case '$':
-               if (check_inverse(optarg, &invert, NULL, 0))
-                       exit_error(PARAMETER_PROBLEM,
-                                  "Unexpected `!' after --limit-burst");
-
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
                if (string_to_number(optarg, 0, 10000, &num) == -1)
                        exit_error(PARAMETER_PROBLEM,
                                   "bad --limit-burst `%s'", optarg);
@@ -126,6 +120,10 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                return 0;
        }
 
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "limit does not support invert");
+
        return 1;
 }
 
@@ -177,20 +175,18 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
                printf("--limit-burst %u ", r->burst);
 }
 
-static
-struct ip6tables_match limit
-= { NULL,
-    "limit",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_rateinfo)),
-    offsetof(struct ip6t_rateinfo, prev),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match limit = {
+       .name           = "limit",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_rateinfo)),
+       .userspacesize  = offsetof(struct ip6t_rateinfo, prev),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_limit.man b/extensions/libip6t_limit.man
new file mode 100644 (file)
index 0000000..84b63d4
--- /dev/null
@@ -0,0 +1,15 @@
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached
+(unless the `!' flag is used).  It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+Maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
index 617b353..e47f21f 100644 (file)
@@ -28,14 +28,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ip6t_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static void
 parse_mac(const char *mac, struct ip6t_mac_info *info)
 {
@@ -128,20 +120,17 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
        print_mac(((struct ip6t_mac_info *)match->data)->srcaddr);
 }
 
-static
-struct ip6tables_match mac
-= { NULL,
-    "mac",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_mac_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_mac_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match mac = {
+       .name           = "mac",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_mac_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_mac_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_mac.man b/extensions/libip6t_mac.man
new file mode 100644 (file)
index 0000000..5321ca1
--- /dev/null
@@ -0,0 +1,10 @@
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address.  It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
index 4aa606e..b831cfe 100644 (file)
@@ -6,7 +6,8 @@
 #include <getopt.h>
 
 #include <ip6tables.h>
-#include <linux/netfilter_ipv6/ip6t_mark.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv6/ip6t_mark.h"
 
 /* Function which prints out usage message. */
 static void
@@ -24,14 +25,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ip6t_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this. */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -46,11 +39,19 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                char *end;
        case '1':
                check_inverse(optarg, &invert, &optind, 0);
+#ifdef KERNEL_64_USERSPACE_32
+               markinfo->mark = strtoull(optarg, &end, 0);
+               if (*end == '/') {
+                       markinfo->mask = strtoull(end+1, &end, 0);
+               } else
+                       markinfo->mask = 0xffffffffffffffffULL;
+#else
                markinfo->mark = strtoul(optarg, &end, 0);
                if (*end == '/') {
                        markinfo->mask = strtoul(end+1, &end, 0);
                } else
                        markinfo->mask = 0xffffffff;
+#endif
                if (*end != '\0' || end == optarg)
                        exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
                if (invert)
@@ -64,17 +65,25 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        return 1;
 }
 
+#ifdef KERNEL_64_USERSPACE_32
 static void
-print_mark(unsigned long mark, unsigned long mask, int invert, int numeric)
+print_mark(unsigned long long mark, unsigned long long mask, int numeric)
+{
+       if(mask != 0xffffffffffffffffULL)
+               printf("0x%llx/0x%llx ", mark, mask);
+       else
+               printf("0x%llx ", mark);
+}
+#else
+static void
+print_mark(unsigned long mark, unsigned long mask, int numeric)
 {
-       if (invert)
-               fputc('!', stdout);
-
        if(mask != 0xffffffff)
                printf("0x%lx/0x%lx ", mark, mask);
        else
                printf("0x%lx ", mark);
 }
+#endif
 
 /* Final check; must have specified --mark. */
 static void
@@ -91,36 +100,40 @@ print(const struct ip6t_ip6 *ip,
       const struct ip6t_entry_match *match,
       int numeric)
 {
+       struct ip6t_mark_info *info = (struct ip6t_mark_info *)match->data;
+
        printf("MARK match ");
-       print_mark(((struct ip6t_mark_info *)match->data)->mark,
-                 ((struct ip6t_mark_info *)match->data)->mask,
-                 ((struct ip6t_mark_info *)match->data)->invert, numeric);
+
+       if (info->invert)
+               printf("!");
+       
+       print_mark(info->mark, info->mask, numeric);
 }
 
 /* Saves the union ip6t_matchinfo in parsable form to stdout. */
 static void
 save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
 {
+       struct ip6t_mark_info *info = (struct ip6t_mark_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+       
        printf("--mark ");
-       print_mark(((struct ip6t_mark_info *)match->data)->mark,
-                 ((struct ip6t_mark_info *)match->data)->mask,
-                 ((struct ip6t_mark_info *)match->data)->invert, 0);
+       print_mark(info->mark, info->mask, 0);
 }
 
-static
-struct ip6tables_match mark
-= { NULL,
-    "mark",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_mark_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_mark_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match mark = {
+       .name           = "mark",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_mark_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_mark_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_mark.man b/extensions/libip6t_mark.man
new file mode 100644 (file)
index 0000000..05f8e1e
--- /dev/null
@@ -0,0 +1,9 @@
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BR "--mark " "\fIvalue\fP[/\fImask\fP]"
+Matches packets with the given unsigned mark value (if a mask is
+specified, this is logically ANDed with the mask before the
+comparison).
index dc5bbf4..013241b 100644 (file)
@@ -112,33 +112,37 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch (c) {
        case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                multiinfo->count = parse_multi_ports(argv[optind-1],
                                                     multiinfo->ports, proto);
                multiinfo->flags = IP6T_MULTIPORT_SOURCE;
-               *nfcache |= NFC_IP6_SRC_PT;
                break;
 
        case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                multiinfo->count = parse_multi_ports(argv[optind-1],
                                                     multiinfo->ports, proto);
                multiinfo->flags = IP6T_MULTIPORT_DESTINATION;
-               *nfcache |= NFC_IP6_DST_PT;
                break;
 
        case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                multiinfo->count = parse_multi_ports(argv[optind-1],
                                                     multiinfo->ports, proto);
                multiinfo->flags = IP6T_MULTIPORT_EITHER;
-               *nfcache |= NFC_IP6_SRC_PT | NFC_IP6_DST_PT;
                break;
 
        default:
                return 0;
        }
 
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport does not support invert");
+
        if (*flags)
                exit_error(PARAMETER_PROBLEM,
                           "multiport can only have one option");
@@ -242,20 +246,18 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
        printf(" ");
 }
 
-static
-struct ip6tables_match multiport
-= { NULL,
-    "multiport",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_multiport)),
-    IP6T_ALIGN(sizeof(struct ip6t_multiport)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match multiport = {
+       .name           = "multiport",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_multiport)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_multiport)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void
diff --git a/extensions/libip6t_multiport.man b/extensions/libip6t_multiport.man
new file mode 100644 (file)
index 0000000..684f49f
--- /dev/null
@@ -0,0 +1,20 @@
+This module matches a set of source or destination ports.  Up to 15
+ports can be specified.  A port range (port:port) counts as two
+ports.  It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the source port is one of the given ports.  The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the destination port is one of the given ports.  The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the both the source and destination ports are equal to each
+other and to one of the given ports.
index 638074d..19b13f7 100644 (file)
@@ -50,13 +50,6 @@ static struct option opts[] = {
        { 0 }
 };
 
-/* Initialize the target. */
-static void
-init(struct ip6t_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
 #define IP6T_NTH_OPT_EVERY     0x01
 #define IP6T_NTH_OPT_NOT_EVERY 0x02
 #define IP6T_NTH_OPT_START     0x04
@@ -217,19 +210,17 @@ save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
                 printf("--packet %u ", nthinfo->packet );
 }
 
-struct ip6tables_match nth
-= { NULL,
-    "nth",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_nth_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_nth_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct ip6tables_match nth = {
+       .name           = "nth",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_nth_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_nth_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_nth.man b/extensions/libip6t_nth.man
new file mode 100644 (file)
index 0000000..d215fd5
--- /dev/null
@@ -0,0 +1,14 @@
+This module matches every `n'th packet
+.TP
+.BI "--every " "value"
+Match every `value' packet
+.TP
+.BI "[" "--counter " "num" "]"
+Use internal counter number `num'.  Default is `0'.
+.TP
+.BI "[" "--start " "num" "]"
+Initialize the counter at the number `num' insetad of `0'.  Most between `0'
+and `value'-1.
+.TP
+.BI "[" "--packet " "num" "]"
+Match on `num' packet.  Most be between `0' and `value'-1.
index ed78530..99b5c13 100644 (file)
@@ -47,14 +47,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ip6t_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this. */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -129,6 +121,7 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                        exit_error(PARAMETER_PROBLEM, "OWNER CMD `%s' too long, max %d characters", optarg, sizeof(ownerinfo->comm));
                
                strncpy(ownerinfo->comm, optarg, sizeof(ownerinfo->comm));
+               ownerinfo->comm[sizeof(ownerinfo->comm)-1] = '\0';
 
                if (invert)
                        ownerinfo->invert |= IP6T_OWNER_COMM;
@@ -148,11 +141,11 @@ print_item(struct ip6t_owner_info *info, u_int8_t flag, int numeric, char *label
 {
        if(info->match & flag) {
 
-               printf(label);
-
                if (info->invert & flag)
                        printf("! ");
 
+               printf(label);
+
                switch(info->match & flag) {
                case IP6T_OWNER_UID:
                        if(!numeric) {
@@ -236,20 +229,17 @@ save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
 #endif
 }
 
-static
-struct ip6tables_match owner
-= { NULL,
-    "owner",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_owner_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_owner_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match owner = {
+       .name           = "owner",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_owner_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_owner_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_owner.man b/extensions/libip6t_owner.man
new file mode 100644 (file)
index 0000000..99680a6
--- /dev/null
@@ -0,0 +1,23 @@
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets.  It is only valid in the
+.B OUTPUT
+chain, and even this some packets (such as ICMP ping responses) may
+have no owner, and hence never match.  This is regarded as experimental.
+.TP
+.BI "--uid-owner " "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner " "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner " "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner " "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.TP
+.B NOTE: pid, sid and command matching are broken on SMP
diff --git a/extensions/libip6t_physdev.c b/extensions/libip6t_physdev.c
new file mode 100644 (file)
index 0000000..fb47347
--- /dev/null
@@ -0,0 +1,191 @@
+/* Shared library add-on to iptables to add bridge port matching support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_physdev.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+static void
+help(void)
+{
+       printf(
+"physdev v%s options:\n"
+" --physdev-in [!] input name[+]               bridge port name ([+] for wildcard)\n"
+" --physdev-out [!] output name[+]     bridge port name ([+] for wildcard)\n"
+" [!] --physdev-is-in                  arrived on a bridge device\n"
+" [!] --physdev-is-out                 will leave on a bridge device\n"
+" [!] --physdev-is-bridged             it's a bridged packet\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "physdev-in", 1, 0, '1' },
+       { "physdev-out", 1, 0, '2' },
+       { "physdev-is-in", 0, 0, '3' },
+       { "physdev-is-out", 0, 0, '4' },
+       { "physdev-is-bridged", 0, 0, '5' },
+       {0}
+};
+
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_physdev_info *info =
+               (struct ip6t_physdev_info*)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_PHYSDEV_OP_IN)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_interface(argv[optind-1], info->physindev, info->in_mask);
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_IN;
+               info->bitmask |= IP6T_PHYSDEV_OP_IN;
+               *flags |= IP6T_PHYSDEV_OP_IN;
+               break;
+
+       case '2':
+               if (*flags & IP6T_PHYSDEV_OP_OUT)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_interface(argv[optind-1], info->physoutdev,
+                               info->out_mask);
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_OUT;
+               info->bitmask |= IP6T_PHYSDEV_OP_OUT;
+               *flags |= IP6T_PHYSDEV_OP_OUT;
+               break;
+
+       case '3':
+               if (*flags & IP6T_PHYSDEV_OP_ISIN)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               info->bitmask |= IP6T_PHYSDEV_OP_ISIN;
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_ISIN;
+               *flags |= IP6T_PHYSDEV_OP_ISIN;
+               break;
+
+       case '4':
+               if (*flags & IP6T_PHYSDEV_OP_ISOUT)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               info->bitmask |= IP6T_PHYSDEV_OP_ISOUT;
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_ISOUT;
+               *flags |= IP6T_PHYSDEV_OP_ISOUT;
+               break;
+
+       case '5':
+               if (*flags & IP6T_PHYSDEV_OP_BRIDGED)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_BRIDGED;
+               *flags |= IP6T_PHYSDEV_OP_BRIDGED;
+               info->bitmask |= IP6T_PHYSDEV_OP_BRIDGED;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+multiple_use:
+       exit_error(PARAMETER_PROBLEM,
+          "multiple use of the same physdev option is not allowed");
+
+}
+
+static void final_check(unsigned int flags)
+{
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM, "PHYSDEV: no physdev option specified");
+}
+
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ip6t_physdev_info *info =
+               (struct ip6t_physdev_info*)match->data;
+
+       printf("PHYSDEV match");
+       if (info->bitmask & IP6T_PHYSDEV_OP_ISIN)
+               printf("%s --physdev-is-in",
+                      info->invert & IP6T_PHYSDEV_OP_ISIN ? " !":"");
+       if (info->bitmask & IP6T_PHYSDEV_OP_IN)
+               printf("%s --physdev-in %s",
+               (info->invert & IP6T_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+       if (info->bitmask & IP6T_PHYSDEV_OP_ISOUT)
+               printf("%s --physdev-is-out",
+                      info->invert & IP6T_PHYSDEV_OP_ISOUT ? " !":"");
+       if (info->bitmask & IP6T_PHYSDEV_OP_OUT)
+               printf("%s --physdev-out %s",
+               (info->invert & IP6T_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+       if (info->bitmask & IP6T_PHYSDEV_OP_BRIDGED)
+               printf("%s --physdev-is-bridged",
+                      info->invert & IP6T_PHYSDEV_OP_BRIDGED ? " !":"");
+       printf(" ");
+}
+
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ip6t_physdev_info *info =
+               (struct ip6t_physdev_info*)match->data;
+
+       if (info->bitmask & IP6T_PHYSDEV_OP_ISIN)
+               printf("%s --physdev-is-in",
+                      info->invert & IP6T_PHYSDEV_OP_ISIN ? " !":"");
+       if (info->bitmask & IP6T_PHYSDEV_OP_IN)
+               printf("%s --physdev-in %s",
+               (info->invert & IP6T_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+       if (info->bitmask & IP6T_PHYSDEV_OP_ISOUT)
+               printf("%s --physdev-is-out",
+                      info->invert & IP6T_PHYSDEV_OP_ISOUT ? " !":"");
+       if (info->bitmask & IP6T_PHYSDEV_OP_OUT)
+               printf("%s --physdev-out %s",
+               (info->invert & IP6T_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+       if (info->bitmask & IP6T_PHYSDEV_OP_BRIDGED)
+               printf("%s --physdev-is-bridged",
+                      info->invert & IP6T_PHYSDEV_OP_BRIDGED ? " !":"");
+       printf(" ");
+}
+
+static struct ip6tables_match physdev = {
+       .name           = "physdev",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_physdev_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_physdev_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&physdev);
+}
diff --git a/extensions/libip6t_physdev.man b/extensions/libip6t_physdev.man
new file mode 100644 (file)
index 0000000..846ec7c
--- /dev/null
@@ -0,0 +1,42 @@
+This module matches on the bridge port input and output devices enslaved
+to a bridge device. This module is a part of the infrastructure that enables
+a transparent bridging IP firewall and is only useful for kernel versions
+above version 2.5.44.
+.TP
+.B --physdev-in name
+Name of a bridge port via which a packet is received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). If the interface name ends in a "+", then any
+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
+.B --physdev-out name
+Name of a bridge port via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+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 it is yet unknown what
+the output device will be, then the packet won't match this option, unless
+'!' is used.
+.TP
+.B --physdev-is-in
+Matches if the packet has entered through a bridge interface.
+.TP
+.B --physdev-is-out
+Matches if the packet will leave through a bridge interface.
+.TP
+.B --physdev-is-bridged
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
index 5f151ea..d34a230 100644 (file)
@@ -52,7 +52,6 @@ static void
 init(struct ip6t_entry_match *m, unsigned int *nfcache)
 {
        struct ip6t_rand_info *randinfo = (struct ip6t_rand_info *)(m)->data;
-       *nfcache |= NFC_UNKNOWN;
 
        /* We assign the average to be 50 which is our default value */
        /* 50 * 2.55 = 128 */
@@ -131,19 +130,18 @@ save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
        printf("--average %u ", result.quot);
 }
 
-struct ip6tables_match rand_match
-= { NULL,
-    "random",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_rand_info)),
-    IP6T_ALIGN(sizeof(struct ip6t_rand_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct ip6tables_match rand_match = {
+       .name           = "random",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_rand_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_rand_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
diff --git a/extensions/libip6t_random.man b/extensions/libip6t_random.man
new file mode 100644 (file)
index 0000000..f808a77
--- /dev/null
@@ -0,0 +1,4 @@
+This module randomly matches a certain percentage of all packets.
+.TP
+.BI "--average " "percent"
+Matches the given percentage.  If omitted, a probability of 50% is set. 
index 06cab16..251604b 100644 (file)
@@ -341,20 +341,18 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
 
 }
 
-static
-struct ip6tables_match rt
-= { NULL,
-    "rt",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_rt)),
-    IP6T_ALIGN(sizeof(struct ip6t_rt)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match rt = {
+       .name           = "rt",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_rt)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_rt)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void
diff --git a/extensions/libip6t_rt.man b/extensions/libip6t_rt.man
new file mode 100644 (file)
index 0000000..4347ecd
--- /dev/null
@@ -0,0 +1,19 @@
+Match on IPv6 routing header
+.TP
+.BI "--rt-type " "[!]" "type"
+Match the type (numeric).
+.TP
+.BI "--rt-segsleft" "[!]" "num[:num]"
+Match the `segments left' field (range).
+.TP
+.BI "--rt-len" "[!]" "length"
+Match the length of this header
+.TP
+.BI "--rt-0-res"
+Match the reserved field, too (type=0)
+.TP
+.BI "--rt-0-addrs ADDR[,ADDR...]
+Match type=0 addresses (list).
+.TP
+.BI "--rt-0-not-strict"
+List of type=0 addresses is not a strict list.
index b8a0ce7..c48882f 100644 (file)
@@ -47,20 +47,17 @@ save(const struct ip6t_ip6 *ip6, const struct ip6t_entry_target *target)
 {
 }
 
-static
-struct ip6tables_target standard
-= { NULL,
-    "standard",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(int)),
-    IP6T_ALIGN(sizeof(int)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    NULL, /* print */
-    &save,
-    opts
+static struct ip6tables_target standard = {
+       .name           = "standard",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(int)),
+       .userspacesize  = IP6T_ALIGN(sizeof(int)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void _init(void)
index a1a912c..a049128 100644 (file)
@@ -187,7 +187,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        tcpinfo->invflags |= IP6T_TCP_INV_SRCPT;
                *flags |= TCP_SRC_PORTS;
-               *nfcache |= NFC_IP6_SRC_PT;
                break;
 
        case '2':
@@ -199,7 +198,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        tcpinfo->invflags |= IP6T_TCP_INV_DSTPT;
                *flags |= TCP_DST_PORTS;
-               *nfcache |= NFC_IP6_DST_PT;
                break;
 
        case '3':
@@ -209,7 +207,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                                   " allowed");
                parse_tcp_flags(tcpinfo, "SYN,RST,ACK", "SYN", invert);
                *flags |= TCP_FLAGS;
-               *nfcache |= NFC_IP6_TCPFLAGS;
                break;
 
        case '4':
@@ -228,7 +225,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                                invert);
                optind++;
                *flags |= TCP_FLAGS;
-               *nfcache |= NFC_IP6_TCPFLAGS;
                break;
 
        case '5':
@@ -240,7 +236,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        tcpinfo->invflags |= IP6T_TCP_INV_OPTION;
                *flags |= TCP_OPTION;
-               *nfcache |= NFC_IP6_PROTO_UNKNOWN;
                break;
 
        default:
@@ -424,20 +419,19 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
        }
 }
 
-static
-struct ip6tables_match tcp
-= { NULL,
-    "tcp",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_tcp)),
-    IP6T_ALIGN(sizeof(struct ip6t_tcp)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts };
+static struct ip6tables_match tcp = {
+       .name           = "tcp",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_tcp)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_tcp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
 
 void
 _init(void)
diff --git a/extensions/libip6t_tcp.man b/extensions/libip6t_tcp.man
new file mode 100644 (file)
index 0000000..75d172e
--- /dev/null
@@ -0,0 +1,45 @@
+These extensions are loaded if `--protocol tcp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is a convenient alias for this option.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.  The flag
+.B --dport
+is a convenient alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified.  The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set.  Flags are: 
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ ip6tables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK and RST bits
+cleared.  Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
index 139386c..842581d 100644 (file)
@@ -109,7 +109,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        udpinfo->invflags |= IP6T_UDP_INV_SRCPT;
                *flags |= UDP_SRC_PORTS;
-               *nfcache |= NFC_IP6_SRC_PT;
                break;
 
        case '2':
@@ -121,7 +120,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        udpinfo->invflags |= IP6T_UDP_INV_DSTPT;
                *flags |= UDP_DST_PORTS;
-               *nfcache |= NFC_IP6_DST_PT;
                break;
 
        default:
@@ -233,20 +231,18 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
        }
 }
 
-static
-struct ip6tables_match udp
-= { NULL,
-    "udp",
-    IPTABLES_VERSION,
-    IP6T_ALIGN(sizeof(struct ip6t_udp)),
-    IP6T_ALIGN(sizeof(struct ip6t_udp)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct ip6tables_match udp = {
+       .name           = "udp",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_udp)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_udp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
 };
 
 void
diff --git a/extensions/libip6t_udp.man b/extensions/libip6t_udp.man
new file mode 100644 (file)
index 0000000..0408479
--- /dev/null
@@ -0,0 +1,14 @@
+These extensions are loaded if `--protocol udp' is specified.  It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
index 7f0b916..6d6392f 100644 (file)
@@ -35,8 +35,6 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
        /* Actually, it's 0, but it's ignored at the moment. */
        mr->rangesize = 1;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Parses range of IPs */
@@ -131,20 +129,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        printf("-%s ", addr_to_dotted(&a));
 }
 
-static
-struct iptables_target balance
-= { NULL,
-    "BALANCE",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target balance = { 
+       .next           = NULL,
+       .name           = "BALANCE",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_BALANCE.man b/extensions/libipt_BALANCE.man
new file mode 100644 (file)
index 0000000..0eb09d0
--- /dev/null
@@ -0,0 +1,4 @@
+This allows you to DNAT connections in a round-robin way over a given range of destination addresses.
+.TP
+.BI "--to-destination " "ipaddr-ipaddr"
+Address range to round-robin over.
index ac14b76..16c8053 100644 (file)
@@ -108,20 +108,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
               TC_H_MAJ(clinfo->priority)>>16, TC_H_MIN(clinfo->priority));
 }
 
-static
-struct iptables_target classify
-= { NULL,
-    "CLASSIFY",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_classify_target_info)),
-    IPT_ALIGN(sizeof(struct ipt_classify_target_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target classify = { 
+       .next           = NULL,
+       .name           = "CLASSIFY",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_classify_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_classify_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_CLASSIFY.man b/extensions/libipt_CLASSIFY.man
new file mode 100644 (file)
index 0000000..393c329
--- /dev/null
@@ -0,0 +1,4 @@
+This module allows you to set the skb->priority value (and thus classify the packet into a specific CBQ class).
+.TP
+.BI "--set-class " "MAJOR:MINOR"
+Set the major and minor class value.
diff --git a/extensions/libipt_CLUSTERIP.c b/extensions/libipt_CLUSTERIP.c
new file mode 100644 (file)
index 0000000..d2bee97
--- /dev/null
@@ -0,0 +1,259 @@
+/* Shared library add-on to iptables to add CLUSTERIP target support. 
+ * (C) 2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ * Development of this code was funded by SuSE AG, http://www.suse.com/
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stddef.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include "../include/linux/netfilter_ipv4/ipt_CLUSTERIP.h"
+
+static void
+help(void)
+{
+       printf(
+"CLUSTERIP target v%s options:\n"
+"  --new                        Create a new ClusterIP\n"
+"  --hashmode <mode>            Specify hashing mode\n"
+"                                      sourceip\n"
+"                                      sourceip-sourceport\n"
+"                                      sourceip-sourceport-destport\n"
+"  --clustermac <mac>           Set clusterIP MAC address\n"
+"  --total-nodes <num>          Set number of total nodes in cluster\n"
+"  --local-node <num>           Set the local node number\n"
+"  --hash-init\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "new", 0, 0, '1' },
+       { "hashmode", 1, 0, '2' },
+       { "clustermac", 1, 0, '3' },
+       { "total-nodes", 1, 0, '4' },
+       { "local-node", 1, 0, '5' },
+       { 0 }
+};
+
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+static void
+parse_mac(const char *mac, char *macbuf)
+{
+       unsigned int i = 0;
+
+       if (strlen(mac) != ETH_ALEN*3-1)
+               exit_error(PARAMETER_PROBLEM, "Bad mac address `%s'", mac);
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               long number;
+               char *end;
+
+               number = strtol(mac + i*3, &end, 16);
+
+               if (end == mac + i*3 + 2
+                   && number >= 0
+                   && number <= 255)
+                       macbuf[i] = number;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad mac address `%s'", mac);
+       }
+}
+
+#define        PARAM_NEW       0x0001
+#define PARAM_HMODE    0x0002
+#define PARAM_MAC      0x0004
+#define PARAM_TOTALNODE        0x0008
+#define PARAM_LOCALNODE        0x0010
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_clusterip_tgt_info *cipinfo
+               = (struct ipt_clusterip_tgt_info *)(*target)->data;
+
+       switch (c) {
+               unsigned int num;
+       case '1':
+               cipinfo->flags |= CLUSTERIP_FLAG_NEW;
+               if (*flags & PARAM_NEW)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify `--new' once\n");
+               *flags |= PARAM_NEW;
+               break;
+       case '2':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify hashmode combined with `--new'\n");
+               if (*flags & PARAM_HMODE)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify hashmode once\n");
+               if (!strcmp(optarg, "sourceip"))
+                       cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP;
+               else if (!strcmp(optarg, "sourceip-sourceport"))
+                       cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT;
+               else if (!strcmp(optarg, "sourceip-sourceport-destport"))
+                       cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT_DPT;
+               else
+                       exit_error(PARAMETER_PROBLEM, "Unknown hashmode `%s'\n",
+                                  optarg);
+               *flags |= PARAM_HMODE;
+               break;
+       case '3':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify MAC combined with `--new'\n");
+               if (*flags & PARAM_MAC)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify MAC once\n");
+               parse_mac(optarg, cipinfo->clustermac);
+               if (!(cipinfo->clustermac[0] & 0x01))
+                       exit_error(PARAMETER_PROBLEM, "MAC has to be a multicast ethernet address\n");
+               *flags |= PARAM_MAC;
+               break;
+       case '4':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify node number combined with `--new'\n");
+               if (*flags & PARAM_TOTALNODE)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify total node number once\n");
+               if (string_to_number(optarg, 1, CLUSTERIP_MAX_NODES, &num) < 0)
+                       exit_error(PARAMETER_PROBLEM, "Unable to parse `%s'\n", optarg);
+               cipinfo->num_total_nodes = (u_int16_t)num;
+               *flags |= PARAM_TOTALNODE;
+               break;
+       case '5':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify node number combined with `--new'\n");
+               if (*flags & PARAM_LOCALNODE)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify local node number once\n");
+               if (string_to_number(optarg, 1, CLUSTERIP_MAX_NODES, &num) < 0)
+                       exit_error(PARAMETER_PROBLEM, "Unable to parse `%s'\n", optarg);
+               cipinfo->num_local_nodes = 1;
+               cipinfo->local_nodes[0] = (u_int16_t)num;
+               *flags |= PARAM_LOCALNODE;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (flags == 0)
+               return;
+
+       if (flags == (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE))
+               return;
+
+       exit_error(PARAMETER_PROBLEM, "CLUSTERIP target: Invalid parameter combination\n");
+}
+
+static char *hashmode2str(enum clusterip_hashmode mode)
+{
+       char *retstr;
+       switch (mode) {
+               case CLUSTERIP_HASHMODE_SIP:
+                       retstr = "sourceip";
+                       break;
+               case CLUSTERIP_HASHMODE_SIP_SPT:
+                       retstr = "sourceip-sourceport";
+                       break;
+               case CLUSTERIP_HASHMODE_SIP_SPT_DPT:
+                       retstr = "sourceip-sourceport-destport";
+                       break;
+               default:
+                       retstr = "unknown-error";
+                       break;
+       }
+       return retstr;
+}
+
+static char *mac2str(const u_int8_t mac[ETH_ALEN])
+{
+       static char buf[ETH_ALEN*3];
+       sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
+               mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+       return buf;
+}
+                       
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_clusterip_tgt_info *cipinfo =
+               (const struct ipt_clusterip_tgt_info *)target->data;
+       
+       if (!cipinfo->flags & CLUSTERIP_FLAG_NEW) {
+               printf("CLUSTERIP");
+               return;
+       }
+
+       printf("CLUSTERIP hashmode=%s clustermac=%s total_nodes=%u local_node=%u ", 
+               hashmode2str(cipinfo->hash_mode),
+               mac2str(cipinfo->clustermac),
+               cipinfo->num_total_nodes,
+               cipinfo->local_nodes[0]);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       /*
+       const struct ipt_connmark_target_info *markinfo =
+               (const struct ipt_connmark_target_info *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_CONNMARK_SET:
+           printf("--set-mark 0x%lx ", markinfo->mark);
+           break;
+       case IPT_CONNMARK_SAVE:
+           printf("--save-mark ");
+           break;
+       case IPT_CONNMARK_RESTORE:
+           printf("--restore-mark ");
+           break;
+       default:
+           printf("ERROR: UNKNOWN CONNMARK MODE ");
+           break;
+       }
+       */
+}
+
+static struct iptables_target clusterip = { 
+       .next           = NULL,
+       .name           = "CLUSTERIP",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)),
+       .userspacesize  = offsetof(struct ipt_clusterip_tgt_info, config),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&clusterip);
+}
diff --git a/extensions/libipt_CLUSTERIP.man b/extensions/libipt_CLUSTERIP.man
new file mode 100644 (file)
index 0000000..8e766f3
--- /dev/null
@@ -0,0 +1,24 @@
+This module allows you to configure a simple cluster of nodes that share
+a certain IP and MAC address without an explicit load balancer in front of
+them.  Connections are statically distributed between the nodes in this
+cluster.
+.TP
+.BI "--new "
+Create a new ClusterIP.  You always have to set this on the first rule
+for a given ClusterIP.
+.TP
+.BI "--hashmode " "mode"
+Specify the hashing mode.  Has to be one of
+.B sourceip, sourceip-sourceport, sourceip-sourceport-destport
+.TP
+.BI "--clustermac " "mac"
+Specify the ClusterIP MAC address.  Has to be a link-layer multicast address
+.TP
+.BI "--total-nodes " "num"
+Number of total nodes within this cluster.
+.TP
+.BI "--local-node " "num"
+Local node number within this cluster.
+.TP
+.BI "--hash-init " "rnd"
+Specify the random seed used for hash initialization.
index 5222182..bc739fc 100644 (file)
@@ -1,4 +1,24 @@
-/* Shared library add-on to iptables to add CONNMARK target support. */
+/* Shared library add-on to iptables to add CONNMARK target support.
+ *
+ * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * Version 1.1
+ *
+ * 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
+ */
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -6,7 +26,7 @@
 
 #include <iptables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ipt_CONNMARK.h>
+#include "../include/linux/netfilter_ipv4/ipt_CONNMARK.h"
 
 #if 0
 struct markinfo {
@@ -21,9 +41,9 @@ help(void)
 {
        printf(
 "CONNMARK target v%s options:\n"
-"  --set-mark value              Set conntrack mark value\n"
-"  --save-mark                   Save the packet nfmark on the connection\n"
-"  --restore-mark                Restore saved nfmark value\n"
+"  --set-mark value[/mask]       Set conntrack mark value\n"
+"  --save-mark [--mask mask]     Save the packet nfmark in the connection\n"
+"  --restore-mark [--mask mask]  Restore saved nfmark value\n"
 "\n",
 IPTABLES_VERSION);
 }
@@ -32,6 +52,7 @@ static struct option opts[] = {
        { "set-mark", 1, 0, '1' },
        { "save-mark", 0, 0, '2' },
        { "restore-mark", 0, 0, '3' },
+       { "mask", 1, 0, '4' },
        { 0 }
 };
 
@@ -51,11 +72,25 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        struct ipt_connmark_target_info *markinfo
                = (struct ipt_connmark_target_info *)(*target)->data;
 
+#ifdef KERNEL_64_USERSPACE_32
+       markinfo->mask = ~0ULL;
+#else
+       markinfo->mask = ~0UL;
+#endif
+
        switch (c) {
                char *end;
        case '1':
                markinfo->mode = IPT_CONNMARK_SET;
+#ifdef KERNEL_64_USERSPACE_32
+               markinfo->mark = strtoull(optarg, &end, 0);
+               if (*end == '/' && end[1] != '\0')
+                   markinfo->mask = strtoull(end+1, &end, 0);
+#else
                markinfo->mark = strtoul(optarg, &end, 0);
+               if (*end == '/' && end[1] != '\0')
+                   markinfo->mask = strtoul(end+1, &end, 0);
+#endif
                if (*end != '\0' || end == optarg)
                        exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
                if (*flags)
@@ -77,6 +112,18 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                                   "CONNMARK target: Can't specify --restore-mark twice");
                *flags = 1;
                break;
+       case '4':
+               if (!*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --mask without a operation");
+#ifdef KERNEL_64_USERSPACE_32
+               markinfo->mask = strtoull(optarg, &end, 0);
+#else
+               markinfo->mask = strtoul(optarg, &end, 0);
+#endif
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MASK value `%s'", optarg);
+               break;
        default:
                return 0;
        }
@@ -89,16 +136,41 @@ final_check(unsigned int flags)
 {
        if (!flags)
                exit_error(PARAMETER_PROBLEM,
-                          "CONNMARK target: Parameter --set-mark is required");
+                          "CONNMARK target: No operation specified");
+}
+
+#ifdef KERNEL_64_USERSPACE_32
+static void
+print_mark(unsigned long long mark)
+{
+       printf("0x%llx", mark);
 }
 
 static void
-print_mark(unsigned long mark, int numeric)
+print_mask(const char *text, unsigned long long mask)
 {
-       printf("0x%lx ", mark);
+       if (mask != ~0ULL)
+               printf("%s0x%llx", text, mask);
 }
 
-/* Prints out the targinfo. */
+#else
+
+static void
+print_mark(unsigned long mark)
+{
+       printf("0x%lx", mark);
+}
+
+static void
+print_mask(const char *text, unsigned long mask)
+{
+       if (mask != ~0UL)
+               printf("%s0x%lx", text, mask);
+}
+#endif
+
+
+/* Prints out the target info. */
 static void
 print(const struct ipt_ip *ip,
       const struct ipt_entry_target *target,
@@ -109,13 +181,18 @@ print(const struct ipt_ip *ip,
        switch (markinfo->mode) {
        case IPT_CONNMARK_SET:
            printf("CONNMARK set ");
-           print_mark(markinfo->mark, numeric);
+           print_mark(markinfo->mark);
+           print_mask("/", markinfo->mask);
+           printf(" ");
            break;
        case IPT_CONNMARK_SAVE:
            printf("CONNMARK save ");
+           print_mask("mask ", markinfo->mask);
+           printf(" ");
            break;
        case IPT_CONNMARK_RESTORE:
            printf("CONNMARK restore ");
+           print_mask("mask ", markinfo->mask);
            break;
        default:
            printf("ERROR: UNKNOWN CONNMARK MODE ");
@@ -123,7 +200,7 @@ print(const struct ipt_ip *ip,
        }
 }
 
-/* Saves the union ipt_targinfo in parsable form to stdout. */
+/* Saves the target into in parsable form to stdout. */
 static void
 save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
 {
@@ -132,13 +209,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
 
        switch (markinfo->mode) {
        case IPT_CONNMARK_SET:
-           printf("--set-mark 0x%lx ", markinfo->mark);
+           printf("--set-mark ");
+           print_mark(markinfo->mark);
+           print_mask("/", markinfo->mask);
+           printf(" ");
            break;
        case IPT_CONNMARK_SAVE:
            printf("--save-mark ");
+           print_mask("--mask ", markinfo->mask);
            break;
        case IPT_CONNMARK_RESTORE:
            printf("--restore-mark ");
+           print_mask("--mask ", markinfo->mask);
            break;
        default:
            printf("ERROR: UNKNOWN CONNMARK MODE ");
@@ -146,23 +228,21 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        }
 }
 
-static
-struct iptables_target mark
-= { NULL,
-    "CONNMARK",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_connmark_target_info)),
-    IPT_ALIGN(sizeof(struct ipt_connmark_target_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target connmark_target = {
+    .name          = "CONNMARK",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_connmark_target_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_connmark_target_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
 };
 
 void _init(void)
 {
-       register_target(&mark);
+       register_target(&connmark_target);
 }
diff --git a/extensions/libipt_CONNMARK.man b/extensions/libipt_CONNMARK.man
new file mode 100644 (file)
index 0000000..8b4de5a
--- /dev/null
@@ -0,0 +1,15 @@
+This module sets the netfilter mark value associated with a connection
+.TP
+.B --set-mark mark[/mask]
+Set connection mark. If a mask is specified then only those bits set in the
+mask is modified.
+.TP
+.B --save-mark [--mask mask]
+Copy the netfilter packet mark value to the connection mark. If a mask
+is specified then only those bits are copied.
+.TP
+.B --restore-mark [--mask mask]
+Copy the connection mark value to the packet. If a mask is specified
+then only those bits are copied. This is only valid in the
+.B mangle
+table.
index 9b82675..9e4f525 100644 (file)
@@ -33,14 +33,6 @@ static struct option opts[] = {
        { 0 }
 };
 
-/* Initialize the target. */
-static void
-init(struct ipt_entry_target *t, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static struct ipt_natinfo *
 append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
 {
@@ -65,7 +57,7 @@ static struct ipt_entry_target *
 parse_to(char *arg, int portok, struct ipt_natinfo *info)
 {
        struct ip_nat_range range;
-       char *colon, *dash;
+       char *colon, *dash, *error;
        struct in_addr *ip;
 
        memset(&range, 0, sizeof(range));
@@ -81,10 +73,15 @@ parse_to(char *arg, int portok, struct ipt_natinfo *info)
                range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
 
                port = atoi(colon+1);
-               if (port == 0 || port > 65535)
+               if (port <= 0 || port > 65535)
                        exit_error(PARAMETER_PROBLEM,
                                   "Port `%s' not valid\n", colon+1);
 
+               error = strchr(colon+1, ':');
+               if (error)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid port:port syntax - use dash\n");
+
                dash = strchr(colon, '-');
                if (!dash) {
                        range.min.tcp.port
@@ -94,7 +91,7 @@ parse_to(char *arg, int portok, struct ipt_natinfo *info)
                        int maxport;
 
                        maxport = atoi(dash + 1);
-                       if (maxport == 0 || maxport > 65535)
+                       if (maxport <= 0 || maxport > 65535)
                                exit_error(PARAMETER_PROBLEM,
                                           "Port `%s' not valid\n", dash+1);
                        if (maxport < port)
@@ -224,20 +221,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        }
 }
 
-static
-struct iptables_target dnat
-= { NULL,
-    "DNAT",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target dnat = { 
+       .next           = NULL,
+       .name           = "DNAT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_DNAT.man b/extensions/libipt_DNAT.man
new file mode 100644 (file)
index 0000000..7579e14
--- /dev/null
@@ -0,0 +1,27 @@
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  It specifies that the destination address of the packet
+should be modified (and all future packets in this connection will
+also be mangled), and rules should cease being examined.  It takes one
+type of option:
+.TP
+.BR "--to-destination " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+which can specify a single new destination IP address, an inclusive
+range of IP addresses, and optionally, a port range (which is only
+valid if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then the destination port will never be
+modified.
+.RS
+.PP
+You can add several --to-destination options.  If you specify more
+than one destination address, either via an address range or multiple
+--to-destination options, a simple round-robin (one after another in
+cycle) load balancing takes place between these adresses.
index f3bf079..90e2a34 100644 (file)
@@ -35,7 +35,7 @@ static void help(void)
 "                              or in hex (ex: 0x20)\n"
 "  --set-dscp-class class      Set the DSCP field in packet header to the\n"
 "                              value represented by the DiffServ class value.\n"
-"                              This class may be EF,BE or any of the CSxx "
+"                              This class may be EF,BE or any of the CSxx\n"
 "                              or AFxx classes.\n"
 "\n"
 "                              These two options are mutually exclusive !\n"
@@ -143,20 +143,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        printf("--set-dscp 0x%02x ", dinfo->dscp);
 }
 
-static
-struct iptables_target dscp
-= { NULL,
-    "DSCP",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_DSCP_info)),
-    IPT_ALIGN(sizeof(struct ipt_DSCP_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target dscp = { 
+       .next           = NULL,
+       .name           = "DSCP",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_DSCP_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_DSCP_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_DSCP.man b/extensions/libipt_DSCP.man
new file mode 100644 (file)
index 0000000..e8e5cf5
--- /dev/null
@@ -0,0 +1,9 @@
+This target allows to alter the value of the DSCP bits within the TOS
+header of the IPv4 packet.  As this manipulates a packet, it can only
+be used in the mangle table.
+.TP
+.BI "--set-dscp " "value"
+Set the DSCP field to a numerical value (can be decimal or hex)
+.TP
+.BI "--set-dscp-class " "class"
+Set the DSCP field to a DiffServ class.
index 205964e..7e8d0c4 100644 (file)
@@ -6,7 +6,7 @@
  *
  * libipt_ECN.c borrowed heavily from libipt_DSCP.c
  *
- * $Id: libipt_ECN.c,v 1.12 2003/01/13 12:35:28 laforge Exp $
+ * $Id: libipt_ECN.c 3507 2004-12-28 13:11:59Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=rusty/emailAddress=rusty@netfilter.org $
  */
 #include <stdio.h>
 #include <string.h>
@@ -164,19 +164,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
 }
 
 static
-struct iptables_target ecn
-= { NULL,
-    "ECN",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_ECN_info)),
-    IPT_ALIGN(sizeof(struct ipt_ECN_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct iptables_target ecn = { 
+       .next           = NULL,
+       .name           = "ECN",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ECN_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ECN_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_ECN.man b/extensions/libipt_ECN.man
new file mode 100644 (file)
index 0000000..3668490
--- /dev/null
@@ -0,0 +1,7 @@
+This target allows to selectively work around known ECN blackholes.
+It can only be used in the mangle table.
+.TP
+.BI "--ecn-tcp-remove"
+Remove all ECN bits from the TCP header.  Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
index 3ff2d5a..62df4cd 100644 (file)
@@ -112,20 +112,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        printf("--set-ftos 0x%02x ", finfo->ftos);
 }
 
-static
-struct iptables_target ftos
-= { NULL,
-    "FTOS",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_FTOS_info)),
-    IPT_ALIGN(sizeof(struct ipt_FTOS_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target ftos = {
+       .next           = NULL,
+       .name           = "FTOS",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_FTOS_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_FTOS_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
index ae335c4..3e0942d 100644 (file)
@@ -53,7 +53,6 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
        ipmarkinfo->andmask=0xffffffff;
        ipmarkinfo->ormask=0;
 
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Function which parses command options; returns true if it
@@ -148,20 +147,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
          printf("--or-mask 0x%lx ", ipmarkinfo->ormask);
 }
 
-static
-struct iptables_target ipmark
-= { NULL,
-    "IPMARK",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_ipmark_target_info)),
-    IPT_ALIGN(sizeof(struct ipt_ipmark_target_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target ipmark = { 
+       .next           = NULL,
+       .name           = "IPMARK",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ipmark_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ipmark_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_IPMARK.man b/extensions/libipt_IPMARK.man
new file mode 100644 (file)
index 0000000..e4659b0
--- /dev/null
@@ -0,0 +1,45 @@
+Allows you to mark a received packet basing on its IP address. This
+can replace many mangle/mark entries with only one, if you use
+firewall based classifier.
+
+This target is to be used inside the mangle table, in the PREROUTING,
+POSTROUTING or FORWARD hooks.
+.TP
+.BI "--addr " "src/dst"
+Use source or destination IP address.
+.TP
+.BI "--and-mask " "mask"
+Perform bitwise `and' on the IP address and this mask.
+.TP
+.BI "--or-mask " "mask"
+Perform bitwise `or' on the IP address and this mask.
+.P
+The order of IP address bytes is reversed to meet "human order of bytes":
+192.168.0.1 is 0xc0a80001. At first the `and' operation is performed, then
+`or'.
+
+Examples:
+
+We create a queue for each user, the queue number is adequate
+to the IP address of the user, e.g.: all packets going to/from 192.168.5.2
+are directed to 1:0502 queue, 192.168.5.12 -> 1:050c etc.
+
+We have one classifier rule:
+.IP
+tc filter add dev eth3 parent 1:0 protocol ip fw
+.P
+Earlier we had many rules just like below:
+.IP
+iptables -t mangle -A POSTROUTING -o eth3 -d 192.168.5.2 -j MARK
+--set-mark 0x10502
+.IP
+iptables -t mangle -A POSTROUTING -o eth3 -d 192.168.5.3 -j MARK
+--set-mark 0x10503
+.P
+Using IPMARK target we can replace all the mangle/mark rules with only one:
+.IP
+iptables -t mangle -A POSTROUTING -o eth3 -j IPMARK --addr=dst
+--and-mask=0xffff --or-mask=0x10000
+.P
+On the routers with hundreds of users there should be significant load
+decrease (e.g. twice).
index f1dcec0..d0305e6 100644 (file)
 #include <iptables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 
-static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
-{
-        *nfcache |= NFC_UNKNOWN;
-}
-
 static void help(void) 
 {
        printf("IPV4OPTSSTRIP v%s target takes no option !! Make sure you use it in the mangle table.\n",
@@ -59,20 +54,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        /* nothing to print, we don't take option... */
 }
 
-static
-struct iptables_target IPV4OPTSSTRIP
-= { NULL,
-    "IPV4OPTSSTRIP",
-    IPTABLES_VERSION,
-    IPT_ALIGN(0),
-    IPT_ALIGN(0),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target IPV4OPTSSTRIP = { 
+       .next           = NULL,
+       .name           = "IPV4OPTSSTRIP",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_IPV4OPTSSTRIP.man b/extensions/libipt_IPV4OPTSSTRIP.man
new file mode 100644 (file)
index 0000000..a17d8a2
--- /dev/null
@@ -0,0 +1,5 @@
+Strip all the IP options from a packet.
+
+The target doesn't take any option, and therefore is extremly easy to use :
+
+# iptables -t mangle -A PREROUTING -j IPV4OPTSSTRIP
index f8c8e4b..5e5d3fd 100644 (file)
 
 #define LOG_DEFAULT_LEVEL LOG_WARNING
 
+#ifndef IPT_LOG_UID /* Old kernel */
+#define IPT_LOG_UID    0x08    /* Log UID owning local socket */
+#undef  IPT_LOG_MASK
+#define IPT_LOG_MASK   0x0f
+#endif
+
 /* Function which prints out usage message. */
 static void
 help(void)
@@ -21,7 +27,8 @@ help(void)
 " --log-prefix prefix          Prefix log messages with this prefix.\n\n"
 " --log-tcp-sequence           Log TCP sequence numbers.\n\n"
 " --log-tcp-options            Log TCP options.\n\n"
-" --log-ip-options             Log IP options.\n\n",
+" --log-ip-options             Log IP options.\n\n"
+" --log-uid                    Log UID owning the local socket.\n\n",
 IPTABLES_VERSION);
 }
 
@@ -31,6 +38,7 @@ static struct option opts[] = {
        { .name = "log-tcp-sequence", .has_arg = 0, .flag = 0, .val = '1' },
        { .name = "log-tcp-options",  .has_arg = 0, .flag = 0, .val = '2' },
        { .name = "log-ip-options",   .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = "log-uid",          .has_arg = 0, .flag = 0, .val = '4' },
        { .name = 0 }
 };
 
@@ -42,8 +50,6 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
 
        loginfo->level = LOG_DEFAULT_LEVEL;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 struct ipt_log_names {
@@ -98,6 +104,7 @@ parse_level(const char *level)
 #define IPT_LOG_OPT_TCPSEQ 0x04
 #define IPT_LOG_OPT_TCPOPT 0x08
 #define IPT_LOG_OPT_IPOPT 0x10
+#define IPT_LOG_OPT_UID 0x20
 
 /* Function which parses command options; returns true if it
    ate an option */
@@ -134,7 +141,11 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
                        exit_error(PARAMETER_PROBLEM,
                                   "Maximum prefix length %u for --log-prefix",
-                                  sizeof(loginfo->prefix) - 1);
+                                  (unsigned int)sizeof(loginfo->prefix) - 1);
+
+               if (strlen(optarg) != strlen(strtok(optarg, "\n")))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Newlines not allowed in --log-prefix");
 
                strcpy(loginfo->prefix, optarg);
                *flags |= IPT_LOG_OPT_PREFIX;
@@ -168,6 +179,15 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                *flags |= IPT_LOG_OPT_IPOPT;
                break;
 
+       case '4':
+               if (*flags & IPT_LOG_OPT_UID)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-uid twice");
+
+               loginfo->logflags |= IPT_LOG_UID;
+               *flags |= IPT_LOG_OPT_UID;
+               break;
+
        default:
                return 0;
        }
@@ -211,6 +231,8 @@ print(const struct ipt_ip *ip,
                        printf("tcp-options ");
                if (loginfo->logflags & IPT_LOG_IPOPT)
                        printf("ip-options ");
+               if (loginfo->logflags & IPT_LOG_UID)
+                       printf("uid ");
                if (loginfo->logflags & ~(IPT_LOG_MASK))
                        printf("unknown-flags ");
        }
@@ -238,6 +260,8 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
                printf("--log-tcp-options ");
        if (loginfo->logflags & IPT_LOG_IPOPT)
                printf("--log-ip-options ");
+       if (loginfo->logflags & IPT_LOG_UID)
+               printf("--log-uid ");
 }
 
 static
diff --git a/extensions/libipt_LOG.man b/extensions/libipt_LOG.man
new file mode 100644 (file)
index 0000000..597ba3f
--- /dev/null
@@ -0,0 +1,31 @@
+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 header fields) via the kernel log
+(where it can be read with
+.I dmesg
+or 
+.IR syslogd (8)).
+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
+then DROP (or REJECT).
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 29 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IP packet header.
+.TP
+.B --log-uid
+Log the userid of the process which generated the packet.
index 0863041..457f6ad 100644 (file)
@@ -6,12 +6,8 @@
 
 #include <iptables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ipt_MARK.h>
-
-struct markinfo {
-       struct ipt_entry_target t;
-       struct ipt_mark_target_info mark;
-};
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_MARK.h"
 
 /* Function which prints out usage message. */
 static void
@@ -20,12 +16,16 @@ help(void)
        printf(
 "MARK target v%s options:\n"
 "  --set-mark value                   Set nfmark value\n"
+"  --and-mark value                   Binary AND the nfmark with value\n"
+"  --or-mark  value                   Binary OR  the nfmark with value\n"
 "\n",
 IPTABLES_VERSION);
 }
 
 static struct option opts[] = {
        { "set-mark", 1, 0, '1' },
+       { "and-mark", 1, 0, '2' },
+       { "or-mark", 1, 0, '3' },
        { 0 }
 };
 
@@ -38,24 +38,34 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
 /* Function which parses command options; returns true if it
    ate an option */
 static int
-parse(int c, char **argv, int invert, unsigned int *flags,
-      const struct ipt_entry *entry,
-      struct ipt_entry_target **target)
+parse_v0(int c, char **argv, int invert, unsigned int *flags,
+        const struct ipt_entry *entry,
+        struct ipt_entry_target **target)
 {
        struct ipt_mark_target_info *markinfo
                = (struct ipt_mark_target_info *)(*target)->data;
 
        switch (c) {
        case '1':
-               if (string_to_number(optarg, 0, 0xffffffff, 
-                                    (unsigned int *)&markinfo->mark))
+#ifdef KERNEL_64_USERSPACE_32
+               if (string_to_number_ll(optarg, 0, 0, 
+                                    &markinfo->mark))
+#else
+               if (string_to_number_l(optarg, 0, 0, 
+                                    &markinfo->mark))
+#endif
                        exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
                if (*flags)
                        exit_error(PARAMETER_PROBLEM,
                                   "MARK target: Can't specify --set-mark twice");
                *flags = 1;
                break;
-
+       case '2':
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: kernel too old for --and-mark");
+       case '3':
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: kernel too old for --or-mark");
        default:
                return 0;
        }
@@ -68,54 +78,166 @@ final_check(unsigned int flags)
 {
        if (!flags)
                exit_error(PARAMETER_PROBLEM,
-                          "MARK target: Parameter --set-mark is required");
+                          "MARK target: Parameter --set/and/or-mark"
+                          " is required");
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse_v1(int c, char **argv, int invert, unsigned int *flags,
+        const struct ipt_entry *entry,
+        struct ipt_entry_target **target)
+{
+       struct ipt_mark_target_info_v1 *markinfo
+               = (struct ipt_mark_target_info_v1 *)(*target)->data;
+
+       switch (c) {
+       case '1':
+               markinfo->mode = IPT_MARK_SET;
+               break;
+       case '2':
+               markinfo->mode = IPT_MARK_AND;
+               break;
+       case '3':
+               markinfo->mode = IPT_MARK_OR;
+               break;
+       default:
+               return 0;
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       if (string_to_number_ll(optarg, 0, 0,  &markinfo->mark))
+#else
+       if (string_to_number_l(optarg, 0, 0, &markinfo->mark))
+#endif
+               exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: Can't specify --set-mark twice");
+
+       *flags = 1;
+       return 1;
 }
 
+#ifdef KERNEL_64_USERSPACE_32
 static void
-print_mark(unsigned long mark, int numeric)
+print_mark(unsigned long long mark)
+{
+       printf("0x%llx ", mark);
+}
+#else
+static void
+print_mark(unsigned long mark)
 {
        printf("0x%lx ", mark);
 }
+#endif
 
 /* Prints out the targinfo. */
 static void
-print(const struct ipt_ip *ip,
-      const struct ipt_entry_target *target,
-      int numeric)
+print_v0(const struct ipt_ip *ip,
+        const struct ipt_entry_target *target,
+        int numeric)
 {
        const struct ipt_mark_target_info *markinfo =
                (const struct ipt_mark_target_info *)target->data;
        printf("MARK set ");
-       print_mark(markinfo->mark, numeric);
+       print_mark(markinfo->mark);
 }
 
 /* Saves the union ipt_targinfo in parsable form to stdout. */
 static void
-save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+save_v0(const struct ipt_ip *ip, const struct ipt_entry_target *target)
 {
        const struct ipt_mark_target_info *markinfo =
                (const struct ipt_mark_target_info *)target->data;
 
-       printf("--set-mark 0x%lx ", markinfo->mark);
+       printf("--set-mark ");
+       print_mark(markinfo->mark);
 }
 
+/* Prints out the targinfo. */
+static void
+print_v1(const struct ipt_ip *ip,
+        const struct ipt_entry_target *target,
+        int numeric)
+{
+       const struct ipt_mark_target_info_v1 *markinfo =
+               (const struct ipt_mark_target_info_v1 *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_MARK_SET:
+               printf("MARK set ");
+               break;
+       case IPT_MARK_AND:
+               printf("MARK and ");
+               break;
+       case IPT_MARK_OR: 
+               printf("MARK or ");
+               break;
+       }
+       print_mark(markinfo->mark);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save_v1(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_mark_target_info_v1 *markinfo =
+               (const struct ipt_mark_target_info_v1 *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_MARK_SET:
+               printf("--set-mark ");
+               break;
+       case IPT_MARK_AND:
+               printf("--and-mark ");
+               break;
+       case IPT_MARK_OR: 
+               printf("--or-mark ");
+               break;
+       }
+       print_mark(markinfo->mark);
+}
+
+static
+struct iptables_target mark_v0 = {
+       .next           = NULL,
+       .name           = "MARK",
+       .version        = IPTABLES_VERSION,
+       .revision       = 0,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse_v0,
+       .final_check    = &final_check,
+       .print          = &print_v0,
+       .save           = &save_v0,
+       .extra_opts     = opts
+};
+
 static
-struct iptables_target mark
-= { NULL,
-    "MARK",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
-    IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct iptables_target mark_v1 = {
+       .next           = NULL,
+       .name           = "MARK",
+       .version        = IPTABLES_VERSION,
+       .revision       = 1,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mark_target_info_v1)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mark_target_info_v1)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse_v1,
+       .final_check    = &final_check,
+       .print          = &print_v1,
+       .save           = &save_v1,
+       .extra_opts     = opts
 };
 
 void _init(void)
 {
-       register_target(&mark);
+       register_target(&mark_v0);
+       register_target(&mark_v1);
 }
diff --git a/extensions/libipt_MARK.man b/extensions/libipt_MARK.man
new file mode 100644 (file)
index 0000000..1c47e97
--- /dev/null
@@ -0,0 +1,6 @@
+This is used to set the netfilter mark value associated with the
+packet.  It is only valid in the
+.B mangle
+table.  It can for example be used in conjunction with iproute2.
+.TP
+.BI "--set-mark " "mark"
index c30e2fa..b661012 100644 (file)
@@ -33,8 +33,6 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
        /* Actually, it's 0, but it's ignored at the moment. */
        mr->rangesize = 1;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Parses ports */
@@ -146,20 +144,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        }
 }
 
-static
-struct iptables_target masq
-= { NULL,
-    "MASQUERADE",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target masq = { NULL,
+       .name           = "MASQUERADE",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_MASQUERADE.man b/extensions/libipt_MASQUERADE.man
new file mode 100644 (file)
index 0000000..e82063c
--- /dev/null
@@ -0,0 +1,22 @@
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain.  It should only be used with dynamically assigned IP (dialup)
+connections: if you have a static IP address, you should use the SNAT
+target.  Masquerading is equivalent to specifying a mapping to the IP
+address of the interface the packet is going out, but also has the
+effect that connections are
+.I forgotten
+when the interface goes down.  This is the correct behavior when the
+next dialup is unlikely to have the same interface address (and hence
+any established connections are lost anyway).  It takes one option:
+.TP
+.BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+This specifies a range of source ports to use, overriding the default
+.B SNAT
+source port-selection heuristics (see above).  This is only valid
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" .
index b5996fa..7e61703 100644 (file)
@@ -41,20 +41,19 @@ final_check(unsigned int flags)
 {
 }
 
-static
-struct iptables_target mirror
-= { NULL,
-    "MIRROR",
-    IPTABLES_VERSION,
-    IPT_ALIGN(0),
-    IPT_ALIGN(0),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    NULL, /* print */
-    NULL, /* save */
-    opts
+static struct iptables_target mirror = {
+       .next           = NULL,
+       .name           = "MIRROR",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = NULL,
+       .save           = NULL,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_MIRROR.man b/extensions/libipt_MIRROR.man
new file mode 100644 (file)
index 0000000..7b720bc
--- /dev/null
@@ -0,0 +1,12 @@
+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.
index 9e71990..403c413 100644 (file)
@@ -32,7 +32,6 @@ static void init(struct ipt_entry_target *t, unsigned int *nfcache)
        
        nld->flags=0;
        
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Parse command options */
@@ -136,19 +135,19 @@ print(const struct ipt_ip *ip,
                printf("nlsize %i ", nld->size);
 }
 
-static
-struct iptables_target netlink = { NULL,
-       "NETLINK",
-       IPTABLES_VERSION,
-       IPT_ALIGN(sizeof(struct ipt_nldata)),
-       IPT_ALIGN(sizeof(struct ipt_nldata)),
-       &help,
-       &init,
-       &parse,
-       &final_check,
-       &print,
-       &save,
-       opts
+static struct iptables_target netlink = {
+       .next           = NULL,
+       .name           = "NETLINK",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_nldata)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_nldata)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
index a45acb9..a39c731 100644 (file)
@@ -63,8 +63,6 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
        /* Actually, it's 0, but it's ignored at the moment. */
        mr->rangesize = 1;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Parses network address */
@@ -179,20 +177,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        print(ip, target, 0);
 }
 
-static
-struct iptables_target target_module
-= { NULL,
-    MODULENAME,
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target target_module = {
+       .next           = NULL,
+       .name           = MODULENAME,
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_NETMAP.man b/extensions/libipt_NETMAP.man
new file mode 100644 (file)
index 0000000..d49a025
--- /dev/null
@@ -0,0 +1,9 @@
+This target allows you to statically map a whole network of addresses onto
+another network of addresses.  It can only be used from rules in the
+.B nat
+table.
+.TP
+.BI "--to "  "address[/mask]"
+Network address to map to.  The resulting address will be constructed in the
+following way: All 'one' bits in the mask are filled in from the new `address'.
+All bits that are zero in the mask are filled in from the original address.
diff --git a/extensions/libipt_NOTRACK.man b/extensions/libipt_NOTRACK.man
new file mode 100644 (file)
index 0000000..30e830a
--- /dev/null
@@ -0,0 +1,5 @@
+This target disables connection tracking for all packets matching that rule.
+.TP
+It can only be used in the
+.B raw
+table.
index 052b533..1395f62 100644 (file)
@@ -33,8 +33,6 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
        /* Actually, it's 0, but it's ignored at the moment. */
        mr->rangesize = 1;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Parses ports */
@@ -147,20 +145,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        }
 }
 
-static
-struct iptables_target redir
-= { NULL,
-    "REDIRECT",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target redir = { 
+       .next           = NULL,
+       .name           = "REDIRECT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_REDIRECT.man b/extensions/libipt_REDIRECT.man
new file mode 100644 (file)
index 0000000..aeca3cb
--- /dev/null
@@ -0,0 +1,19 @@
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  It redirects the packet to the machine itself by changing the
+destination IP to the primary address of the incoming interface
+(locally-generated packets are mapped to the 127.0.0.1 address).  It
+takes one option:
+.TP
+.BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+This specifies a destination port or range of ports to use: without
+this, the destination port is never altered.  This is only valid
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" .
index 8170edd..f982331 100644 (file)
@@ -94,8 +94,6 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
        /* default */
        reject->with = IPT_ICMP_PORT_UNREACHABLE;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Function which parses command options; returns true if it
@@ -170,20 +168,19 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        printf("--reject-with %s ", reject_table[i].name);
 }
 
-static
-struct iptables_target reject
-= { NULL,
-    "REJECT",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_reject_info)),
-    IPT_ALIGN(sizeof(struct ipt_reject_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target reject = { 
+       .next           = NULL,
+       .name           = "REJECT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_reject_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_reject_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_REJECT.man b/extensions/libipt_REJECT.man
new file mode 100644 (file)
index 0000000..174bf7b
--- /dev/null
@@ -0,0 +1,34 @@
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  The following option controls the nature of the error packet
+returned:
+.TP
+.BI "--reject-with " "type"
+The type given can be
+.nf
+.B " icmp-net-unreachable"
+.B " icmp-host-unreachable"
+.B " icmp-port-unreachable"
+.B " icmp-proto-unreachable"
+.B " icmp-net-prohibited"
+.B " icmp-host-prohibited or"
+.B " icmp-admin-prohibited (*)"
+.fi
+which return the appropriate ICMP error message (\fBport-unreachable\fP is
+the default).  The option
+.B tcp-reset
+can be used on rules which only match the TCP protocol: this causes a
+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).
+.TP
+(*) Using icmp-admin-prohibited with kernels that do not support it will result in a plain DROP instead of REJECT
index 1cb3e89..360f983 100644 (file)
@@ -1,6 +1,6 @@
 /* Shared library add-on to iptables to add ROUTE target support.
  * Author : Cedric de Launois, <delaunois@info.ucl.ac.be>
- * v 1.8 2003/06/24
+ * v 1.11 2004/11/23
  */
 
 #include <stdio.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_ROUTE.h>
 
+/* compile IPT_ROUTE_TEE support even if kernel headers are unpatched */
+#ifndef IPT_ROUTE_TEE
+#define IPT_ROUTE_TEE          0x02
+#endif
+
 /* Function which prints out usage message. */
 static void
 help(void)
 {
        printf(
 "ROUTE target v%s options:\n"
-"    --oif   \tifname \t\tRoute the packet through `ifname' network interface\n"
-"    --iif   \tifname \t\tChange the packet's incoming interface to `ifname'\n"
-"    --gw    \tip     \t\tRoute the packet via this gateway\n"
-"    --continue\t     \t\tRoute the packet and continue traversing the\n"
-"            \t       \t\trules. Not valid with --iif.\n"
+"    --oif   \tifname \t\tRoute packet through `ifname' network interface\n"
+"    --iif   \tifname \t\tChange packet's incoming interface to `ifname'\n"
+"    --gw    \tip     \t\tRoute packet via this gateway `ip'\n"
+"    --continue\t     \t\tRoute packet and continue traversing the\n"
+"            \t       \t\trules. Not valid with --iif or --tee.\n"
+"    --tee\t  \t\tDuplicate packet, route the duplicate,\n"
+"            \t       \t\tcontinue traversing with original packet.\n"
+"            \t       \t\tNot valid with --iif or --continue.\n"
 "\n",
-"1.8");
+"1.11");
 }
 
 static struct option opts[] = {
@@ -35,6 +43,7 @@ static struct option opts[] = {
        { "iif", 1, 0, '2' },
        { "gw", 1, 0, '3' },
        { "continue", 0, 0, '4' },
+       { "tee", 0, 0, '5' },
        { 0 }
 };
 
@@ -56,6 +65,7 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
 #define IPT_ROUTE_OPT_IIF      0x02
 #define IPT_ROUTE_OPT_GW       0x04
 #define IPT_ROUTE_OPT_CONTINUE 0x08
+#define IPT_ROUTE_OPT_TEE      0x10
 
 /* Function which parses command options; returns true if it
    ate an option */
@@ -134,12 +144,28 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (*flags & IPT_ROUTE_OPT_CONTINUE)
                        exit_error(PARAMETER_PROBLEM,
                                   "Can't specify --continue twice");
+               if (*flags & IPT_ROUTE_OPT_TEE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --continue AND --tee");
 
                route_info->flags |= IPT_ROUTE_CONTINUE;
                *flags |= IPT_ROUTE_OPT_CONTINUE;
 
                break;
 
+       case '5':
+               if (*flags & IPT_ROUTE_OPT_TEE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --tee twice");
+               if (*flags & IPT_ROUTE_OPT_CONTINUE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --tee AND --continue");
+
+               route_info->flags |= IPT_ROUTE_TEE;
+               *flags |= IPT_ROUTE_OPT_TEE;
+
+               break;
+
        default:
                return 0;
        }
@@ -155,7 +181,7 @@ final_check(unsigned int flags)
                exit_error(PARAMETER_PROBLEM,
                           "ROUTE target: oif, iif or gw option required");
 
-       if ((flags & IPT_ROUTE_OPT_CONTINUE) && (flags & IPT_ROUTE_OPT_IIF))
+       if ((flags & (IPT_ROUTE_OPT_CONTINUE|IPT_ROUTE_OPT_TEE)) && (flags & IPT_ROUTE_OPT_IIF))
                exit_error(PARAMETER_PROBLEM,
                           "ROUTE target: can't continue traversing the rules with iif option");
 }
@@ -186,6 +212,9 @@ print(const struct ipt_ip *ip,
        if (route_info->flags & IPT_ROUTE_CONTINUE)
                printf("continue");
 
+       if (route_info->flags & IPT_ROUTE_TEE)
+               printf("tee");
+
 }
 
 
@@ -208,23 +237,25 @@ static void save(const struct ipt_ip *ip,
 
        if (route_info->flags & IPT_ROUTE_CONTINUE)
                printf("--continue ");
+
+       if (route_info->flags & IPT_ROUTE_TEE)
+               printf("--tee ");
 }
 
 
-static
-struct iptables_target route
-= { NULL,
-    "ROUTE",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_route_target_info)),
-    IPT_ALIGN(sizeof(struct ipt_route_target_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target route = { 
+       .next           = NULL,
+       .name           = "ROUTE",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_route_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_route_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_ROUTE.man b/extensions/libipt_ROUTE.man
new file mode 100644 (file)
index 0000000..8a36e8e
--- /dev/null
@@ -0,0 +1,18 @@
+This is used to explicitly override the core network stack's routing decision.
+.B mangle
+table.
+.TP
+.BI "--oif " "ifname"
+Route the packet through `ifname' network interface
+.TP
+.BI "--iif " "ifname"
+Change the packet's incoming interface to `ifname'
+.TP
+.BI "--gw " "IP_address"
+Route the packet via this gateway
+.TP
+.BI "--continue "
+Behave like a non-terminating target and continue traversing the rules.  Not valid in combination with `--iif' or `--tee'
+.TP
+.BI "--tee "
+Make a copy of the packet, and route that copy to the given destination. For the original, uncopied packet, behave like a non-terminating target and continue traversing the rules.  Not valid in combination with `--iif' or `--continue'
index e9c42a8..4eda223 100644 (file)
@@ -7,7 +7,8 @@
 #include <iptables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ip_nat_rule.h>
-#include <linux/netfilter_ipv4/ipt_SAME.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_SAME.h"
 
 /* Function which prints out usage message. */
 static void
@@ -42,8 +43,6 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
        mr->info = 0;
        mr->ipnum = 0;
        
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* Parses range of IPs */
@@ -188,20 +187,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
                printf("--nodst ");
 }
 
-static
-struct iptables_target same
-= { NULL,
-    "SAME",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_same_info)),
-    IPT_ALIGN(sizeof(struct ipt_same_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target same = {
+       .next           = NULL,
+       .name           = "SAME",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_same_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_same_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_SAME.man b/extensions/libipt_SAME.man
new file mode 100644 (file)
index 0000000..817c200
--- /dev/null
@@ -0,0 +1,11 @@
+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.
+.TP
+.BI "--to " "<ipaddr>-<ipaddr>"
+Addresses to map source to. May be specified more than once for
+multiple ranges.
+.TP
+.B "--nodst"
+Don't use the destination-ip in the calculations when selecting the
+new source-ip
diff --git a/extensions/libipt_SET.c b/extensions/libipt_SET.c
new file mode 100644 (file)
index 0000000..d11a9f0
--- /dev/null
@@ -0,0 +1,180 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ *                         Patrick Schaaf <bof@bof.de>
+ *                         Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2004 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 version 2 as
+ * published by the Free Software Foundation.  
+ */
+
+/* Shared library add-on to iptables to add IP set mangling target. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+#include <linux/netfilter_ipv4/ipt_set.h>
+#include "libipt_set.h"
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf("SET v%s options:\n"
+              " --add-set name flags\n"
+              " --del-set name flags\n"
+              "                add/del src/dst IP/port from/to named sets,\n"
+              "                where flags are the comma separated list of\n"
+              "                'src' and 'dst'.\n"
+              "\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {"add-set",   1, 0, '1'},
+       {"del-set",   1, 0, '2'},
+       {0}
+};
+
+/* Initialize the target. */
+static void init(struct ipt_entry_target *target, unsigned int *nfcache)
+{
+       struct ipt_set_info_target *info =
+           (struct ipt_set_info_target *) target->data;
+
+       memset(info, 0, sizeof(struct ipt_set_info_target));
+       info->add_set.index =
+       info->del_set.index = IP_SET_INVALID_ID;
+
+}
+
+static void
+parse_target(char **argv, int invert, unsigned int *flags,
+             struct ipt_set_info *info, const char *what)
+{
+       if (info->flags[0])
+               exit_error(PARAMETER_PROBLEM,
+                          "--%s can be specified only once", what);
+
+       if (check_inverse(optarg, &invert, NULL, 0))
+               exit_error(PARAMETER_PROBLEM,
+                          "Unexpected `!' after --%s", what);
+
+       if (!argv[optind]
+           || argv[optind][0] == '-' || argv[optind][0] == '!')
+               exit_error(PARAMETER_PROBLEM,
+                          "--%s requires two args.", what);
+
+       if (strlen(argv[optind-1]) > IP_SET_MAXNAMELEN - 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "setname `%s' too long, max %d characters.",
+                          argv[optind-1], IP_SET_MAXNAMELEN - 1);
+
+       get_set_byname(argv[optind - 1], info);
+       parse_bindings(argv[optind], info);
+       optind++;
+       
+       *flags = 1;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry, struct ipt_entry_target **target)
+{
+       struct ipt_set_info_target *myinfo =
+           (struct ipt_set_info_target *) (*target)->data;
+
+       switch (c) {
+       case '1':               /* --add-set <set> <flags> */
+               parse_target(argv, invert, flags,
+                            &myinfo->add_set, "add-set");
+               break;
+       case '2':               /* --del-set <set>[:<flags>] <flags> */
+               parse_target(argv, invert, flags,
+                            &myinfo->del_set, "del-set");
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must specify at least one. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "You must specify either `--add-set' or `--del-set'");
+}
+
+static void
+print_target(const char *prefix, const struct ipt_set_info *info)
+{
+       int i;
+       char setname[IP_SET_MAXNAMELEN];
+
+       if (info->index == IP_SET_INVALID_ID)
+               return;
+       get_set_byid(setname, info->index);
+       printf("%s %s", prefix, setname);
+       for (i = 0; i < IP_SET_MAX_BINDINGS; i++) {
+               if (!info->flags[i])
+                       break;          
+               printf("%s%s",
+                      i == 0 ? " " : ",",
+                      info->flags[i] & IPSET_SRC ? "src" : "dst");
+       }
+       printf(" ");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target, int numeric)
+{
+       struct ipt_set_info_target *info =
+           (struct ipt_set_info_target *) target->data;
+
+       print_target("add-set", &info->add_set);
+       print_target("del-set", &info->del_set);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       struct ipt_set_info_target *info =
+           (struct ipt_set_info_target *) target->data;
+
+       print_target("--add-set", &info->add_set);
+       print_target("--del-set", &info->del_set);
+}
+
+static
+struct iptables_target ipt_set_target 
+= {
+       .name           = "SET",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_set_info_target)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_set_info_target)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&ipt_set_target);
+}
diff --git a/extensions/libipt_SET.man b/extensions/libipt_SET.man
new file mode 100644 (file)
index 0000000..8f25bea
--- /dev/null
@@ -0,0 +1,16 @@
+This modules adds and/or deletes entries from IP sets which can be defined 
+by ipset(8).
+.TP
+.BR "--add-set " "setname flag[,flag...]"
+add the address(es)/port(s) of the packet to the sets
+.TP
+.BR "--del-set " "setname flag[,flag...]"
+delete the address(es)/port(s) of the packet from the sets,
+where flags are
+.BR "src"
+and/or
+.BR "dst"
+and there can be no more than six of them.
+.TP
+The bindings to follow must previously be defined in order to use 
+multilevel adding/deleting by the SET target.
index d909c2a..a893a47 100644 (file)
@@ -33,14 +33,6 @@ static struct option opts[] = {
        { 0 }
 };
 
-/* Initialize the target. */
-static void
-init(struct ipt_entry_target *t, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static struct ipt_natinfo *
 append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
 {
@@ -65,7 +57,7 @@ static struct ipt_entry_target *
 parse_to(char *arg, int portok, struct ipt_natinfo *info)
 {
        struct ip_nat_range range;
-       char *colon, *dash;
+       char *colon, *dash, *error;
        struct in_addr *ip;
 
        memset(&range, 0, sizeof(range));
@@ -81,10 +73,15 @@ parse_to(char *arg, int portok, struct ipt_natinfo *info)
                range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
 
                port = atoi(colon+1);
-               if (port == 0 || port > 65535)
+               if (port <= 0 || port > 65535)
                        exit_error(PARAMETER_PROBLEM,
                                   "Port `%s' not valid\n", colon+1);
 
+               error = strchr(colon+1, ':');
+               if (error)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid port:port syntax - use dash\n");
+
                dash = strchr(colon, '-');
                if (!dash) {
                        range.min.tcp.port
@@ -94,7 +91,7 @@ parse_to(char *arg, int portok, struct ipt_natinfo *info)
                        int maxport;
 
                        maxport = atoi(dash + 1);
-                       if (maxport == 0 || maxport > 65535)
+                       if (maxport <= 0 || maxport > 65535)
                                exit_error(PARAMETER_PROBLEM,
                                           "Port `%s' not valid\n", dash+1);
                        if (maxport < port)
@@ -224,20 +221,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        }
 }
 
-static
-struct iptables_target snat
-= { NULL,
-    "SNAT",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target snat = {
+       .next           = NULL,
+       .name           = "SNAT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_SNAT.man b/extensions/libipt_SNAT.man
new file mode 100644 (file)
index 0000000..4cc0397
--- /dev/null
@@ -0,0 +1,26 @@
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain.  It specifies that the source address of the packet should be
+modified (and all future packets in this connection will also be
+mangled), and rules should cease being examined.  It takes one type
+of option:
+.TP
+.BR "--to-source  " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+which can specify a single new source IP address, an inclusive range
+of IP addresses, and optionally, a port range (which is only valid if
+the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then source ports below 512 will be
+mapped to other ports below 512: those between 512 and 1023 inclusive
+will be mapped to ports below 1024, and other ports will be mapped to
+1024 or above. Where possible, no port alteration will occur.
+.RS
+.PP
+You can add several --to-source options.  If you specify more
+than one source address, either via an address range or multiple
+--to-source options, a simple round-robin (one after another in
+cycle) takes place between these adresses.
index 643ce61..b12cbc2 100644 (file)
@@ -15,13 +15,6 @@ static struct option opts[] = {
        { 0 }
 };
 
-static void
-init(struct ipt_entry_target *t, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static int
 parse(int c, char **argv, int invert, unsigned int *flags,
       const struct ipt_entry *entry,
@@ -45,20 +38,18 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
 {
 }
 
-static
-struct iptables_target tarpit
-= { NULL,
-    "TARPIT",
-    IPTABLES_VERSION,
-    IPT_ALIGN(0),
-    IPT_ALIGN(0),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target tarpit = {
+       .next           = NULL,
+       .name           = "TARPIT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_TARPIT.man b/extensions/libipt_TARPIT.man
new file mode 100644 (file)
index 0000000..26526b7
--- /dev/null
@@ -0,0 +1,34 @@
+Captures and holds incoming TCP connections using no local
+per-connection resources. Connections are accepted, but immediately
+switched to the persist state (0 byte window), in which the remote
+side stops sending data and asks to continue every 60-240 seconds.
+Attempts to close the connection are ignored, forcing the remote side
+to time out the connection in 12-24 minutes.
+
+This offers similar functionality to LaBrea
+<http://www.hackbusters.net/LaBrea/> but doesn't require dedicated
+hardware or IPs. Any TCP port that you would normally DROP or REJECT
+can instead become a tarpit.
+
+To tarpit connections to TCP port 80 destined for the current machine:
+.IP
+iptables -A INPUT -p tcp -m tcp --dport 80 -j TARPIT
+.P
+To significantly slow down Code Red/Nimda-style scans of unused address
+space, forward unused ip addresses to a Linux box not acting as a router
+(e.g. "ip route 10.0.0.0 255.0.0.0 ip.of.linux.box" on a Cisco), enable IP
+forwarding on the Linux box, and add:
+.IP
+iptables -A FORWARD -p tcp -j TARPIT
+.IP
+iptables -A FORWARD -j DROP
+.TP
+NOTE:
+If you use the conntrack module while you are using TARPIT, you should
+also use the NOTRACK target, or the kernel will unnecessarily allocate
+resources for each TARPITted connection. To TARPIT incoming
+connections to the standard IRC port while using conntrack, you could:
+.IP
+iptables -t raw -A PREROUTING -p tcp --dport 6667 -j NOTRACK
+.IP
+iptables -A INPUT -p tcp --dport 6667 -j TARPIT
index 27361e7..3042d73 100644 (file)
@@ -70,18 +70,6 @@ static const struct option opts[] =
  * our own private data structure (which is at t->data).
  * Probably we could fiddle with t->tflags too but there is
  * no great advantage in doing so.
- * 
- * TODO: Find documentation for the above flags which
- *       can be ored into nfcache...
- *
- * NFC_IP6_DST_PT
- * NFC_IP6_PROTO_UNKNOWN
- * NFC_IP6_SRC_PT
- * NFC_IP6_TCPFLAGS
- * NFC_IP_DST_PT
- * NFC_IP_SRC_PT
- * NFC_IP_TOS
- * NFC_UNKNOWN             -- This one seems safest
  */
 static void init( struct ipt_entry_target *t, unsigned int *nfcache )
 {
@@ -89,7 +77,6 @@ static void init( struct ipt_entry_target *t, unsigned int *nfcache )
        memset( el, 0, sizeof( struct ipt_tcplag ));
        el->level = 4; /* Default to warning level */
        strcpy( el->prefix, "TCPLAG:" ); /* Give a reasonable default prefix */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /*
@@ -208,7 +195,7 @@ static struct iptables_target targ =
 {
 next:            0,
 name:             "TCPLAG",
-version:          "1.2.3",
+version:          IPTABLES_VERSION,
 size:             IPT_ALIGN( sizeof( struct ipt_tcplag )),
 userspacesize:    IPT_ALIGN( sizeof( struct ipt_tcplag )),
 help:             &help,
index 6892b52..c3256f0 100644 (file)
@@ -113,20 +113,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
                printf("--set-mss %u ", mssinfo->mss);
 }
 
-static
-struct iptables_target mss
-= { NULL,
-    "TCPMSS",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_tcpmss_info)),
-    IPT_ALIGN(sizeof(struct ipt_tcpmss_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target mss = {
+       .next           = NULL,
+       .name           = "TCPMSS",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tcpmss_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tcpmss_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_TCPMSS.man b/extensions/libipt_TCPMSS.man
new file mode 100644 (file)
index 0000000..da1bce2
--- /dev/null
@@ -0,0 +1,38 @@
+This target allows to alter the MSS value of TCP SYN packets, to control
+the maximum size for that connection (usually limiting it to your
+outgoing interface's MTU minus 40).  Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
+.br
+This target is used to overcome criminally braindead ISPs or servers
+which block ICMP Fragmentation Needed packets.  The symptoms of this
+problem are that everything works fine from your Linux
+firewall/router, but machines behind it can never exchange large
+packets:
+.PD 0
+.RS 0.1i
+.TP 0.3i
+1)
+Web browsers connect, then hang with no data received.
+.TP
+2)
+Small mail works fine, but large emails hang.
+.TP
+3)
+ssh works fine, but scp hangs after initial handshaking.
+.RE
+.PD
+Workaround: activate this option and add a rule to your firewall
+configuration like:
+.nf
+ iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \\
+             -j TCPMSS --clamp-mss-to-pmtu
+.fi
+.TP
+.BI "--set-mss " "value"
+Explicitly set MSS option to specified value.
+.TP
+.B "--clamp-mss-to-pmtu"
+Automatically clamp MSS value to (path_MTU - 40).
+.TP
+These options are mutually exclusive.
index 87c3816..4302950 100644 (file)
@@ -153,20 +153,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        printf("--set-tos 0x%02x ", tosinfo->tos);
 }
 
-static
-struct iptables_target tos
-= { NULL,
-    "TOS",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_tos_target_info)),
-    IPT_ALIGN(sizeof(struct ipt_tos_target_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_target tos = {
+       .next           = NULL,
+       .name           = "TOS",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tos_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tos_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_TOS.man b/extensions/libipt_TOS.man
new file mode 100644 (file)
index 0000000..c31b068
--- /dev/null
@@ -0,0 +1,11 @@
+This is used to set the 8-bit Type of Service field in the IP header.
+It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-tos " "tos"
+You can use a numeric TOS values, or use
+.nf
+ iptables -j TOS -h
+.fi
+to see the list of valid TOS names.
diff --git a/extensions/libipt_TRACE.man b/extensions/libipt_TRACE.man
new file mode 100644 (file)
index 0000000..549ab33
--- /dev/null
@@ -0,0 +1,3 @@
+This target has no options.  It just turns on 
+.B packet tracing
+for all packets that match this rule.
index 84d6424..a2a28bd 100644 (file)
@@ -1,7 +1,7 @@
 /* Shared library add-on to iptables for the TTL target
  * (C) 2000 by Harald Welte <laforge@gnumonks.org>
  *
- * $Id: libipt_TTL.c,v 1.6 2002/05/29 13:08:16 laforge Exp $
+ * $Id: libipt_TTL.c 3507 2004-12-28 13:11:59Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=rusty/emailAddress=rusty@netfilter.org $
  *
  * This program is distributed under the terms of GNU GPL
  */
@@ -24,9 +24,9 @@ static void help(void)
 {
        printf(
 "TTL target v%s options\n"
-"  --ttl-set value             Set TTL to <value>\n"
-"  --ttl-dec value             Decrement TTL by <value>\n"
-"  --ttl-inc value             Increment TTL by <value>\n"
+"  --ttl-set value             Set TTL to <value 0-255>\n"
+"  --ttl-dec value             Decrement TTL by <value 1-255>\n"
+"  --ttl-inc value             Increment TTL by <value 1-255>\n"
 , IPTABLES_VERSION);
 }
 
@@ -35,7 +35,7 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
                struct ipt_entry_target **target)
 {
        struct ipt_TTL_info *info = (struct ipt_TTL_info *) (*target)->data;
-       u_int8_t value;
+       unsigned int value;
 
        if (*flags & IPT_TTL_USED) {
                exit_error(PARAMETER_PROBLEM, 
@@ -50,7 +50,9 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
                exit_error(PARAMETER_PROBLEM,
                                "TTL: unexpected `!'");
        
-       value = atoi(optarg);
+       if (string_to_number(optarg, 0, 255, &value) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "TTL: Expected value between 0 and 255");
 
        switch (c) {
 
@@ -143,19 +145,19 @@ static struct option opts[] = {
        { 0 }
 };
 
-static
-struct iptables_target TTL = { NULL, 
-       "TTL",
-       IPTABLES_VERSION,
-       IPT_ALIGN(sizeof(struct ipt_TTL_info)),
-       IPT_ALIGN(sizeof(struct ipt_TTL_info)),
-       &help,
-       &init,
-       &parse,
-       &final_check,
-       &print,
-       &save,
-       opts 
+static struct iptables_target TTL = {
+       .next           = NULL, 
+       .name           = "TTL",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_TTL_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_TTL_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts 
 };
 
 void _init(void)
diff --git a/extensions/libipt_TTL.man b/extensions/libipt_TTL.man
new file mode 100644 (file)
index 0000000..97c46c4
--- /dev/null
@@ -0,0 +1,19 @@
+This is used to modify the IPv4 TTL header field.  The TTL field determines
+how many hops (routers) a packet can traverse until it's time to live is
+exceeded.
+.TP
+Setting or incrementing the TTL field can potentially be very dangerous,
+so it should be avoided at any cost.  
+.TP
+.B Don't ever set or increment the value on packets that leave your local network!
+.B mangle
+table.
+.TP
+.BI "--ttl-set " "value"
+Set the TTL value to `value'.
+.TP
+.BI "--ttl-dec " "value"
+Decrement the TTL value `value' times.
+.TP
+.BI "--ttl-inc " "value"
+Increment the TTL value `value' times.
index 41ee991..f4b7a14 100644 (file)
 #include <getopt.h>
 #include <iptables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ipt_ULOG.h>
-
-#define ULOG_DEFAULT_NLGROUP 1
-#define ULOG_DEFAULT_QTHRESHOLD 1
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_ULOG.h"
 
 
 void print_groups(unsigned int gmask)
@@ -62,8 +60,6 @@ static void init(struct ipt_entry_target *t, unsigned int *nfcache)
        loginfo->nl_group = ULOG_DEFAULT_NLGROUP;
        loginfo->qthreshold = ULOG_DEFAULT_QTHRESHOLD;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 #define IPT_LOG_OPT_NLGROUP 0x01
@@ -112,7 +108,7 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
                if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
                        exit_error(PARAMETER_PROBLEM,
                                   "Maximum prefix length %u for --ulog-prefix",
-                                  sizeof(loginfo->prefix) - 1);
+                                  (unsigned int)sizeof(loginfo->prefix) - 1);
 
                strcpy(loginfo->prefix, optarg);
                *flags |= IPT_LOG_OPT_PREFIX;
@@ -124,7 +120,11 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
                if (atoi(optarg) < 0)
                        exit_error(PARAMETER_PROBLEM,
                                   "Negative copy range?");
+#ifdef KERNEL_64_USERSPACE_32
+               loginfo->copy_range = (unsigned long long)atoll(optarg);
+#else
                loginfo->copy_range = atoi(optarg);
+#endif
                *flags |= IPT_LOG_OPT_CPRANGE;
                break;
        case 'B':
@@ -137,9 +137,15 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
                if (atoi(optarg) > ULOG_MAX_QLEN)
                        exit_error(PARAMETER_PROBLEM,
                                   "Maximum queue length exceeded");
+#ifdef KERNEL_64_USERSPACE_32
+               loginfo->qthreshold = (unsigned long long)atoll(optarg);
+#else
                loginfo->qthreshold = atoi(optarg);
+#endif
                *flags |= IPT_LOG_OPT_QTHRESHOLD;
                break;
+       default:
+               return 0;
        }
        return 1;
 }
@@ -163,11 +169,19 @@ static void save(const struct ipt_ip *ip,
                printf("--ulog-nlgroup ");
                print_groups(loginfo->nl_group);
        }
+#ifdef KERNEL_64_USERSPACE_32
+       if (loginfo->copy_range)
+               printf("--ulog-cprange %llu ", loginfo->copy_range);
+
+       if (loginfo->qthreshold != ULOG_DEFAULT_QTHRESHOLD)
+               printf("--ulog-qthreshold %llu ", loginfo->qthreshold);
+#else
        if (loginfo->copy_range)
-               printf("--ulog-cprange %d ", loginfo->copy_range);
+               printf("--ulog-cprange %u ", (unsigned int)loginfo->copy_range);
 
        if (loginfo->qthreshold != ULOG_DEFAULT_QTHRESHOLD)
-               printf("--ulog-qthreshold %d ", loginfo->qthreshold);
+               printf("--ulog-qthreshold %u ", (unsigned int)loginfo->qthreshold);
+#endif
 }
 
 /* Prints out the targinfo. */
@@ -179,26 +193,34 @@ print(const struct ipt_ip *ip,
            = (const struct ipt_ulog_info *) target->data;
 
        printf("ULOG ");
-       printf("copy_range %d nlgroup ", loginfo->copy_range);
+#ifdef KERNEL_64_USERSPACE_32
+       printf("copy_range %llu nlgroup ", loginfo->copy_range);
+#else
+       printf("copy_range %u nlgroup ", (unsigned int)loginfo->copy_range);
+#endif
        print_groups(loginfo->nl_group);
        if (strcmp(loginfo->prefix, "") != 0)
                printf("prefix `%s' ", loginfo->prefix);
-       printf("queue_threshold %d ", loginfo->qthreshold);
+#ifdef KERNEL_64_USERSPACE_32
+       printf("queue_threshold %llu ", loginfo->qthreshold);
+#else
+       printf("queue_threshold %u ", (unsigned int)loginfo->qthreshold);
+#endif
 }
 
-static
-struct iptables_target ulog = { NULL,
-       "ULOG",
-       IPTABLES_VERSION,
-       IPT_ALIGN(sizeof(struct ipt_ulog_info)),
-       IPT_ALIGN(sizeof(struct ipt_ulog_info)),
-       &help,
-       &init,
-       &parse,
-       &final_check,
-       &print,
-       &save,
-       opts
+static struct iptables_target ulog = {
+       .next           = NULL,
+       .name           = "ULOG",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ulog_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ulog_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_ULOG.man b/extensions/libipt_ULOG.man
new file mode 100644 (file)
index 0000000..51aa619
--- /dev/null
@@ -0,0 +1,27 @@
+This target provides userspace logging of matching packets.  When this
+target is set for a rule, the Linux kernel will multicast this packet
+through a
+.IR netlink 
+socket. One or more userspace processes may then subscribe to various 
+multicast groups and receive the packets.
+Like LOG, this is a "non-terminating target", i.e. rule traversal
+continues at the next rule.
+.TP
+.BI "--ulog-nlgroup " "nlgroup"
+This specifies the netlink group (1-32) to which the packet is sent.
+Default value is 1.
+.TP
+.BI "--ulog-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 32 characters
+long, and useful for distinguishing messages in the logs.
+.TP
+.BI "--ulog-cprange " "size"
+Number of bytes to be copied to userspace.  A value of 0 always copies
+the entire packet, regardless of its size.  Default is 0.
+.TP
+.BI "--ulog-qthreshold " "size"
+Number of packet to queue inside kernel.  Setting this value to, e.g. 10
+accumulates ten packets inside the kernel and transmits them as one
+netlink multipart message to userspace.  Default is 1 (for backwards
+compatibility).
+.br
index 3b05a30..2397916 100644 (file)
@@ -47,6 +47,7 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
        switch (c) {    
                case '1':
                        strncpy(info->key, optarg, 30);
+                       info->key[29] = '\0';
                        *flags |= IPT_KEY_SET;
                        break;
                case '2':
@@ -92,18 +93,19 @@ static struct option opts[] = {
        { 0 }
 };
 
-static struct iptables_target XOR = { NULL, 
-       "XOR",
-       IPTABLES_VERSION,
-       IPT_ALIGN(sizeof(struct ipt_XOR_info)),
-       IPT_ALIGN(sizeof(struct ipt_XOR_info)),
-       &help,
-       &init,
-       &parse,
-       &final_check,
-       &print,
-       &save,
-       opts 
+static struct iptables_target XOR = {
+       .next           = NULL, 
+       .name           = "XOR",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_XOR_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_XOR_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts 
 };
 
 void _init(void)
diff --git a/extensions/libipt_XOR.man b/extensions/libipt_XOR.man
new file mode 100644 (file)
index 0000000..712b472
--- /dev/null
@@ -0,0 +1,7 @@
+Encrypt TCP and UDP traffic using a simple XOR encryption
+.TP
+.BI "--key " "string"
+Set key to "string"
+.TP
+.BI "--block-size"
+Set block size
diff --git a/extensions/libipt_account.c b/extensions/libipt_account.c
new file mode 100644 (file)
index 0000000..d049a03
--- /dev/null
@@ -0,0 +1,277 @@
+/* 
+ * accounting match helper (libipt_account.c)
+ * (C) 2003,2004 by Piotr Gasid³o (quaker@barbara.eu.org)
+ *
+ * Version: 0.1.6
+ *
+ * This software is distributed under the terms of GNU GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <iptables.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <linux/netfilter_ipv4/ipt_account.h>
+
+#ifndef HIPQUAD
+#define HIPQUAD(addr) \
+       ((unsigned char *)&addr)[3], \
+       ((unsigned char *)&addr)[2], \
+       ((unsigned char *)&addr)[1], \
+       ((unsigned char *)&addr)[0]
+#endif
+                               
+static void help(void) {
+       printf(
+                       "account v%s options:\n"
+                       "--aaddr network/netmask\n"
+                       "       defines network/netmask for which make statistics.\n"
+                       "--aname name\n"
+                       "       defines name of list where statistics will be kept. If no is\n"
+                       "       specified DEFAULT will be used.\n"
+                       "--ashort\n"
+                       "       table will colect only short statistics (only total counters\n"
+                       "       without splitting it into protocols.\n"
+       , 
+       IPTABLES_VERSION);
+};
+
+static struct option opts[] = {
+       { .name = "aaddr",  .has_arg = 1, .flag = NULL, .val = 201 },
+       { .name = "aname",  .has_arg = 1, .flag = NULL, .val = 202 },
+       { .name = "ashort", .has_arg = 0, .flag = NULL, .val = 203 },
+       { .name = 0, .has_arg = 0, .flag = 0, .val = 0 }
+};
+
+/* Helper functions for parse_network */
+int parseip(const char *parameter, u_int32_t *ip) {
+       
+       char buffer[16], *bufferptr, *dot;
+       unsigned int i, shift, part;
+
+       if (strlen(parameter) > 15)
+               return 0;
+
+       strncpy(buffer, parameter, 15);
+       buffer[15] = 0;
+
+       bufferptr = buffer;
+
+       for (i = 0, shift = 24, *ip = 0; i < 3; i++, shift -= 8) {
+               /* no dot */
+               if ((dot = strchr(bufferptr, '.')) == NULL)
+                       return 0;
+               /* not a number */
+               if ((part = strtol(bufferptr, (char**)NULL, 10)) < 0) 
+                       return 0;       
+               /* to big number */
+               if (part > 255)
+                       return 0;
+               *ip |= part << shift;           
+               bufferptr = dot + 1;
+       }
+       /* not a number */
+       if ((part = strtol(bufferptr, (char**)NULL, 10)) < 0) 
+               return 0;
+       /* to big number */
+       if (part > 255)
+               return 0;
+       *ip |= part;
+       return 1;
+}
+
+static void parsenetwork(const char *parameter, u_int32_t *network) {
+       if (!parseip(parameter, network))
+               exit_error(PARAMETER_PROBLEM, "account: wrong ip in network");
+}
+
+static void parsenetmaskasbits(const char *parameter, u_int32_t *netmask) {
+       
+       u_int32_t bits;
+       
+       if ((bits = strtol(parameter, (char **)NULL, 10)) < 0 || bits > 32)
+               exit_error(PARAMETER_PROBLEM, "account: wrong netmask");
+
+       *netmask = 0xffffffff << (32 - bits);
+}
+
+static void parsenetmaskasip(const char *parameter, u_int32_t *netmask) {
+       if (!parseip(parameter, netmask))
+               exit_error(PARAMETER_PROBLEM, "account: wrong ip in netmask");
+}
+
+static void parsenetmask(const char *parameter, u_int32_t *netmask) 
+{
+       if (strchr(parameter, '.') != NULL)
+               parsenetmaskasip(parameter, netmask);
+       else
+               parsenetmaskasbits(parameter, netmask);
+}
+
+static void parsenetworkandnetmask(const char *parameter, u_int32_t *network, u_int32_t *netmask) 
+{
+       
+       char buffer[32], *slash;
+
+       if (strlen(parameter) > 31)
+               /* text is to long, even for 255.255.255.255/255.255.255.255 */
+               exit_error(PARAMETER_PROBLEM, "account: wrong network/netmask");
+
+       strncpy(buffer, parameter, 31);
+       buffer[31] = 0;
+
+       /* check whether netmask is given */
+       if ((slash = strchr(buffer, '/')) != NULL) {
+               parsenetmask(slash + 1, netmask);
+               *slash = 0;
+       } else
+               *netmask = 0xffffffff;
+       parsenetwork(buffer, network);
+
+       if ((*network & *netmask) != *network)
+               exit_error(PARAMETER_PROBLEM, "account: wrong network/netmask");
+}
+
+
+/* Function gets network & netmask from argument after --aaddr */
+static void parse_network(const char *parameter, struct t_ipt_account_info *info) {
+
+       parsenetworkandnetmask(parameter, &info->network, &info->netmask);
+       
+}
+
+/* validate netmask */
+inline int valid_netmask(u_int32_t netmask) {
+       while (netmask & 0x80000000)
+               netmask <<= 1;
+       if (netmask != 0)
+               return 0;
+        return 1;
+}
+
+/* validate network/netmask pair */
+inline int valid_network_and_netmask(struct t_ipt_account_info *info) {
+       if (!valid_netmask(info->netmask))
+               return 0;
+       if ((info->network & info->netmask) != info->network)
+               return 0;
+       return 1;
+}
+
+
+
+/* Function initializes match */
+static void init(struct ipt_entry_match *match, 
+                unsigned int *nfcache) {
+       
+       struct t_ipt_account_info *info = (struct t_ipt_account_info *)(match)->data;
+
+
+       /* set default table name to DEFAULT */
+       strncpy(info->name, "DEFAULT", IPT_ACCOUNT_NAME_LEN);
+       info->shortlisting = 0;
+       
+}
+
+/* Function parses match's arguments */
+static int parse(int c, char **argv, 
+                 int invert, 
+                 unsigned int *flags,
+                  const struct ipt_entry *entry,
+                  unsigned int *nfcache,
+                  struct ipt_entry_match **match) {
+       
+       struct t_ipt_account_info *info = (struct t_ipt_account_info *)(*match)->data;
+
+       switch (c) {
+               
+               /* --aaddr */
+               case 201:
+                       parse_network(optarg, info);
+                       if (!valid_network_and_netmask(info))
+                               exit_error(PARAMETER_PROBLEM, "account: wrong network/netmask");
+                       *flags = 1;
+                       break;
+                       
+               /* --aname */
+               case 202:
+                       if (strlen(optarg) < IPT_ACCOUNT_NAME_LEN)
+                               strncpy(info->name, optarg, IPT_ACCOUNT_NAME_LEN);
+                       else
+                               exit_error(PARAMETER_PROBLEM, "account: Too long table name");                  
+                       break;  
+               /* --ashort */
+               case 203:
+                       info->shortlisting = 1;
+                       break;
+               default:
+                       return 0;                       
+       }
+       return 1;       
+}
+
+/* Final check whether network/netmask was specified */
+static void final_check(unsigned int flags) {
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "account: You need specify '--aaddr' parameter");
+}
+
+/* Function used for printing rule with account match for iptables -L */
+static void print(const struct ipt_ip *ip,
+                  const struct ipt_entry_match *match, 
+                 int numeric) {
+       
+       struct t_ipt_account_info *info = (struct t_ipt_account_info *)match->data;
+       
+       printf("account: ");
+       printf("network/netmask: ");
+       printf("%u.%u.%u.%u/%u.%u.%u.%u ",
+                       HIPQUAD(info->network),
+                       HIPQUAD(info->netmask)
+             );
+       
+       printf("name: %s ", info->name);
+       if (info->shortlisting)
+               printf("short-listing ");
+}
+
+/* Function used for saving rule containing account match */
+static void save(const struct ipt_ip *ip, 
+                const struct ipt_entry_match *match) {
+
+       struct t_ipt_account_info *info = (struct t_ipt_account_info *)match->data;
+       
+       printf("--aaddr ");
+       printf("%u.%u.%u.%u/%u.%u.%u.%u ",
+                        HIPQUAD(info->network),
+                        HIPQUAD(info->netmask)
+              );
+       
+       printf("--aname %s ", info->name);
+       if (info->shortlisting)
+               printf("--ashort ");
+}
+       
+static struct iptables_match account = {
+       .next = NULL,
+       .name = "account",
+       .version = IPTABLES_VERSION,
+       .size = IPT_ALIGN(sizeof(struct t_ipt_account_info)),
+       .userspacesize = IPT_ALIGN(sizeof(struct t_ipt_account_info)),
+       .help = &help,
+       .init = &init,
+       .parse = &parse,
+       .final_check = &final_check,
+       .print = &print,
+       .save = &save,
+       .extra_opts = opts
+};
+
+/* Function which registers match */
+void _init(void)
+{
+       register_match(&account);
+}
+       
diff --git a/extensions/libipt_account.man b/extensions/libipt_account.man
new file mode 100644 (file)
index 0000000..fcbb179
--- /dev/null
@@ -0,0 +1,47 @@
+Account traffic for all hosts in defined network/netmask.
+
+Features:
+
+- long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics
+
+- one iptables rule for all hosts in network/netmask
+
+- loading/saving counters (by reading/writting to procfs entries)
+
+.TP
+.BI "--aaddr " "network/netmask"
+defines network/netmask for which make statistics.
+.TP
+.BI "--aname " "name"
+defines name of list where statistics will be kept. If no is
+specified DEFAULT will be used.
+.TP
+.B "--ashort"
+table will colect only short statistics (only total counters
+without splitting it into protocols.
+.P
+Example usage:
+
+account traffic for/to 192.168.0.0/24 network into table mynetwork:
+
+# iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24
+
+account traffic for/to WWW serwer for 192.168.0.0/24 network into table mywwwserver:
+
+# iptables -A INPUT -p tcp --dport 80
+  -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
+
+# iptables -A OUTPUT -p tcp --sport 80
+  -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
+
+read counters:
+
+# cat /proc/net/ipt_account/mynetwork
+# cat /proc/net/ipt_account/mywwwserver
+
+set counters:
+
+# echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver
+
+Webpage:
+  http://www.barbara.eu.org/~quaker/ipt_account/
index 093e915..d8e1929 100644 (file)
@@ -48,12 +48,6 @@ static void help(void)
        help_types();
 }
 
-static void init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* caching not yet implemented */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static int
 parse_type(const char *name, size_t strlen, u_int16_t *mask)
 {
@@ -193,18 +187,17 @@ static struct option opts[] = {
 
 static
 struct iptables_match addrtype = {
-       NULL,
-       "addrtype",
-       IPTABLES_VERSION,
-       IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
-       IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
-       &help,
-       &init,
-       &parse,
-       &final_check,
-       &print,
-       &save,
-       opts
+       .next           = NULL,
+       .name           = "addrtype",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 
diff --git a/extensions/libipt_addrtype.man b/extensions/libipt_addrtype.man
new file mode 100644 (file)
index 0000000..2c3bbab
--- /dev/null
@@ -0,0 +1,37 @@
+This module matches packets based on their 
+.B address type.
+Address types are used within the kernel networking stack and categorize
+addresses into various groups.  The exact definition of that group depends on the specific layer three protocol.
+.TP
+The following address types are possible:
+.TP
+.BI "UNSPEC"
+an unspecified address (i.e. 0.0.0.0)
+.BI "UNICAST"
+an unicast address
+.BI "LOCAL"
+a local address
+.BI "BROADCAST"
+a broadcast address
+.BI "ANYCAST"
+an anycast packet
+.BI "MULTICAST"
+a multicast address
+.BI "BLACKHOLE"
+a blackhole address
+.BI "UNREACHABLE"
+an unreachable address
+.BI "PROHIBIT"
+a prohibited address
+.BI "THROW"
+FIXME
+.BI "NAT"
+FIXME
+.BI "XRESOLVE"
+FIXME
+.TP
+.BI "--src-type " "type"
+Matches if the source address is of given type
+.TP
+.BI "--dst-type " "type"
+Matches if the destination address is of given type
index 1e58398..443c9f8 100644 (file)
@@ -168,20 +168,19 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 
 }
 
-static
-struct iptables_match ah
-= { NULL,
-    "ah",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_ah)),
-    IPT_ALIGN(sizeof(struct ipt_ah)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match ah = { 
+       .next           = NULL,
+       .name           = "ah",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ah)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ah)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void
diff --git a/extensions/libipt_ah.man b/extensions/libipt_ah.man
new file mode 100644 (file)
index 0000000..97de1e1
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the SPIs in AH header of IPSec packets.
+.TP
+.BR "--ahspi " "[!] \fIspi\fP[:\fIspi\fP]"
diff --git a/extensions/libipt_childlevel.c b/extensions/libipt_childlevel.c
new file mode 100644 (file)
index 0000000..1018c9e
--- /dev/null
@@ -0,0 +1,115 @@
+/* 
+   Shared library add-on to iptables to add layer 7 matching support. 
+
+   http://l7-filter.sf.net
+  
+   By Matthew Strait <quadong@users.sf.net>, Dec 2003.
+
+   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.
+   http://www.gnu.org/licenses/gpl.txt
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <dirent.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_childlevel.h>
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf(
+       "CHILDLEVEL match v%s options:\n"
+       "--level <n>  : Match childlevel n (0 == master)\n",
+       IPTABLES_VERSION);
+       fputc('\n', stdout);
+}
+
+static struct option opts[] = {
+       { .name = "level", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = 0 }
+};
+
+/* Function which parses command options; returns true if it ate an option */
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry, unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_childlevel_info *childlevelinfo = 
+               (struct ipt_childlevel_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               childlevelinfo->childlevel = atoi(argv[optind-1]);
+               if (invert)
+                       childlevelinfo->invert = 1;
+               *flags = 1;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified --level. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "CHILDLEVEL match: You must specify `--level'");
+}
+
+static void print_protocol(int n, int invert, int numeric)
+{
+       fputs("childlevel ", stdout);
+       if (invert) fputc('!', stdout);
+       printf("%d ", n);
+}
+
+/* Prints out the matchinfo. */
+static void print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       printf("CHILDLEVEL ");
+
+       print_protocol(((struct ipt_childlevel_info *)match->data)->childlevel,
+                 ((struct ipt_childlevel_info *)match->data)->invert, numeric);
+}
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+        const struct ipt_childlevel_info *info =
+            (const struct ipt_childlevel_info*) match->data;
+
+        printf("--childlevel %s%d ", (info->invert) ? "! ": "", info->childlevel);
+}
+
+static struct iptables_match childlevel = { 
+       .name           = "childlevel",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_childlevel_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_childlevel_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&childlevel);
+}
diff --git a/extensions/libipt_childlevel.man b/extensions/libipt_childlevel.man
new file mode 100644 (file)
index 0000000..3d9b355
--- /dev/null
@@ -0,0 +1,5 @@
+This is an experimental module.  It matches on whether the 
+packet is part of a master connection or one of its children (or grandchildren,
+etc).  For instance, most packets are level 0.  FTP data transfer is level 1.
+.TP
+.BR "--childlevel " "[!] \fIlevel\fP"
diff --git a/extensions/libipt_comment.c b/extensions/libipt_comment.c
new file mode 100644 (file)
index 0000000..c543fc6
--- /dev/null
@@ -0,0 +1,119 @@
+/* Shared library add-on to iptables to add comment match support.
+ *
+ * ChangeLog
+ *     2003-05-13: Brad Fisher <brad@info-link.net>
+ *         Initial comment match
+ *     2004-05-12: Brad Fisher <brad@info-link.net>
+ *         Port to patch-o-matic-ng
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_comment.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+               "COMMENT match options:\n"
+               "--comment COMMENT             Attach a comment to a rule\n\n"
+               );
+}
+
+static struct option opts[] = {
+       { "comment", 1, 0, '1' },
+       {0}
+};
+
+static void
+parse_comment(const unsigned char *s, struct ipt_comment_info *info)
+{      
+       int slen = strlen(s);
+
+       if (slen >= IPT_MAX_COMMENT_LEN) {
+               exit_error(PARAMETER_PROBLEM,
+                       "COMMENT must be shorter than %i characters", IPT_MAX_COMMENT_LEN);
+       }
+       strcpy(info->comment, s);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_comment_info *commentinfo = (struct ipt_comment_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               if (invert) {
+                       exit_error(PARAMETER_PROBLEM,
+                                       "Sorry, you can't have an inverted comment");
+               }
+               parse_comment(argv[optind-1], commentinfo);
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --comment. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "COMMENT match: You must specify `--comment'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_comment_info *commentinfo = (struct ipt_comment_info *)match->data;
+
+       commentinfo->comment[IPT_MAX_COMMENT_LEN-1] = '\0';
+       printf("/* %s */ ", commentinfo->comment);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_comment_info *commentinfo = (struct ipt_comment_info *)match->data;
+
+       commentinfo->comment[IPT_MAX_COMMENT_LEN-1] = '\0';
+       printf("--comment \"%s\" ", commentinfo->comment);
+}
+
+static struct iptables_match comment = {
+    .next              = NULL,
+    .name              = "comment",
+    .version           = IPTABLES_VERSION,
+    .size              = IPT_ALIGN(sizeof(struct ipt_comment_info)),
+    .userspacesize     = IPT_ALIGN(sizeof(struct ipt_comment_info)),
+    .help              = &help,
+    .parse             = &parse,
+    .final_check       = &final_check,
+    .print             = &print,
+    .save              = &save,
+    .extra_opts                = opts
+};
+
+void _init(void)
+{
+       register_match(&comment);
+}
diff --git a/extensions/libipt_comment.man b/extensions/libipt_comment.man
new file mode 100644 (file)
index 0000000..2f4ce55
--- /dev/null
@@ -0,0 +1,6 @@
+Allows you to add comments (up to 256 characters) to any rule.
+.TP
+.BI "--comment " "comment"
+.TP
+Example:
+iptables -A INPUT -s 192.168.0.0/16 -m comment --comment "A privatized IP block"
index 750111b..16558fe 100644 (file)
@@ -24,14 +24,6 @@ static struct option opts[] = {
        { .name = 0 }
 };
 
-
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
-
 static int
 parse(int c, char **argv, int invert, unsigned int *flags,
       const struct ipt_entry *entry, unsigned int *nfcache,
@@ -94,17 +86,16 @@ save(const struct ipt_ip *ip,
 
 
 static struct iptables_match condition = {
-       .name = "condition",
-       .version = IPTABLES_VERSION,
-       .size = IPT_ALIGN(sizeof(struct condition_info)),
-       .userspacesize = IPT_ALIGN(sizeof(struct condition_info)),
-       .help = &help,
-       .init = &init,
-       .parse = &parse,
-       .final_check = &final_check,
-       .print = &print,
-       .save = &save,
-       .extra_opts = opts
+       .name           = "condition",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct condition_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct condition_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 
diff --git a/extensions/libipt_condition.man b/extensions/libipt_condition.man
new file mode 100644 (file)
index 0000000..0fc51ff
--- /dev/null
@@ -0,0 +1,4 @@
+This matches if a specific /proc filename is '0' or '1'.
+.TP
+.BI "--condition " "[!] filename"
+Match on boolean value stored in /proc/net/ipt_condition/filename file
index abc37be..7274194 100644 (file)
@@ -15,38 +15,34 @@ help(void)
        printf(
 "connbytes v%s options:\n"
 " [!] --connbytes from:[to]\n"
-"                              Transfered byte range to match\n"
+"     --connbytes-dir [original, reply, both]\n"
+"     --connbytes-mode [packets, bytes, avgpkt]\n"
 "\n", IPTABLES_VERSION);
 }
 
 static struct option opts[] = {
        { "connbytes", 1, 0, '1' },
+       { "connbytes-dir", 1, 0, '2' },
+       { "connbytes-mode", 1, 0, '3' },
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static void
 parse_range(const char *arg, struct ipt_connbytes_info *si)
 {
        char *colon,*p;
 
-       si->from = strtol(arg,&colon,10);
+       si->count.from = strtoul(arg,&colon,10);
        if (*colon != ':') 
                exit_error(PARAMETER_PROBLEM, "Bad range `%s'", arg);
-       si->to = strtol(colon+1,&p,10);
+       si->count.to = strtoul(colon+1,&p,10);
        if (p == colon+1) {
                /* second number omited */
-               si->to = 0xffffffff;
+               si->count.to = 0xffffffff;
        }
-       if (si->from > si->to)
-               exit_error(PARAMETER_PROBLEM, "%lu should be less than %lu", si->from,si->to);
+       if (si->count.from > si->count.to)
+               exit_error(PARAMETER_PROBLEM, "%llu should be less than %llu",
+                          si->count.from, si->count.to);
 }
 
 /* Function which parses command options; returns true if it
@@ -58,22 +54,46 @@ parse(int c, char **argv, int invert, unsigned int *flags,
       struct ipt_entry_match **match)
 {
        struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)(*match)->data;
-       int i;
+       unsigned long i;
 
        switch (c) {
        case '1':
-               if (check_inverse(optarg, &invert, optind, 0))
+               if (check_inverse(optarg, &invert, &optind, 0))
                        optind++;
 
                parse_range(argv[optind-1], sinfo);
                if (invert) {
-                       i = sinfo->from;
-                       sinfo->from = sinfo->to;
-                       sinfo->to = i;
+                       i = sinfo->count.from;
+                       sinfo->count.from = sinfo->count.to;
+                       sinfo->count.to = i;
                }
-               *flags = 1;
+               *flags |= 1;
+               break;
+       case '2':
+               if (!strcmp(optarg, "original"))
+                       sinfo->direction = IPT_CONNBYTES_DIR_ORIGINAL;
+               else if (!strcmp(optarg, "reply"))
+                       sinfo->direction = IPT_CONNBYTES_DIR_REPLY;
+               else if (!strcmp(optarg, "both"))
+                       sinfo->direction = IPT_CONNBYTES_DIR_BOTH;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown --connbytes-dir `%s'", optarg);
+
+               *flags |= 2;
+               break;
+       case '3':
+               if (!strcmp(optarg, "packets"))
+                       sinfo->what = IPT_CONNBYTES_WHAT_PKTS;
+               else if (!strcmp(optarg, "bytes"))
+                       sinfo->what = IPT_CONNBYTES_WHAT_BYTES;
+               else if (!strcmp(optarg, "avgpkt"))
+                       sinfo->what = IPT_CONNBYTES_WHAT_AVGPKT;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown --connbytes-mode `%s'", optarg);
+               *flags |= 4;
                break;
-
        default:
                return 0;
        }
@@ -83,8 +103,45 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
 static void final_check(unsigned int flags)
 {
-       if (!flags)
-               exit_error(PARAMETER_PROBLEM, "You must specify `--connbytes'");
+       if (flags != 7)
+               exit_error(PARAMETER_PROBLEM, "You must specify `--connbytes'"
+                          "`--connbytes-direction' and `--connbytes-mode'");
+}
+
+static void print_mode(struct ipt_connbytes_info *sinfo)
+{
+       switch (sinfo->what) {
+               case IPT_CONNBYTES_WHAT_PKTS:
+                       fputs("packets ", stdout);
+                       break;
+               case IPT_CONNBYTES_WHAT_BYTES:
+                       fputs("bytes ", stdout);
+                       break;
+               case IPT_CONNBYTES_WHAT_AVGPKT:
+                       fputs("avgpkt ", stdout);
+                       break;
+               default:
+                       fputs("unknown ", stdout);
+                       break;
+       }
+}
+
+static void print_direction(struct ipt_connbytes_info *sinfo)
+{
+       switch (sinfo->direction) {
+               case IPT_CONNBYTES_DIR_ORIGINAL:
+                       fputs("original ", stdout);
+                       break;
+               case IPT_CONNBYTES_DIR_REPLY:
+                       fputs("reply ", stdout);
+                       break;
+               case IPT_CONNBYTES_DIR_BOTH:
+                       fputs("both ", stdout);
+                       break;
+               default:
+                       fputs("unknown ", stdout);
+                       break;
+       }
 }
 
 /* Prints out the matchinfo. */
@@ -95,10 +152,18 @@ print(const struct ipt_ip *ip,
 {
        struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data;
 
-       if (sinfo->from > sinfo->to) 
-               printf("connbytes ! %lu:%lu",sinfo->to,sinfo->from);
+       if (sinfo->count.from > sinfo->count.to) 
+               printf("connbytes ! %llu:%llu ", sinfo->count.to,
+                       sinfo->count.from);
        else
-               printf("connbytes %lu:%lu",sinfo->from,sinfo->to);
+               printf("connbytes %llu:%llu ",sinfo->count.from,
+                       sinfo->count.to);
+
+       fputs("connbytes mode ", stdout);
+       print_mode(sinfo);
+
+       fputs("connbytes direction ", stdout);
+       print_direction(sinfo);
 }
 
 /* Saves the matchinfo in parsable form to stdout. */
@@ -106,26 +171,32 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 {
        struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data;
 
-       if (sinfo->from > sinfo->to) 
-               printf("! --connbytes %lu:%lu",sinfo->to,sinfo->from);
+       if (sinfo->count.from > sinfo->count.to) 
+               printf("! --connbytes %llu:%llu ", sinfo->count.to,
+                       sinfo->count.from);
        else
-               printf("--connbytes %lu:%lu",sinfo->from,sinfo->to);
+               printf("--connbytes %llu:%llu ", sinfo->count.from,
+                       sinfo->count.to);
+
+       fputs("--connbytes-mode ", stdout);
+       print_mode(sinfo);
+
+       fputs("--connbytes-direction ", stdout);
+       print_direction(sinfo);
 }
 
-static
-struct iptables_match state
-= { NULL,
-    "connbytes",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
-    IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match state = {
+       .next           = NULL,
+       .name           = "connbytes",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_connbytes.man b/extensions/libipt_connbytes.man
new file mode 100644 (file)
index 0000000..ce7b665
--- /dev/null
@@ -0,0 +1,30 @@
+Match by how many bytes or packets a connection (or one of the two
+flows constituting the connection) have tranferred so far, or by
+average bytes per packet.
+
+The counters are 64bit and are thus not expected to overflow ;)
+
+The primary use is to detect long-lived downloads and mark them to be
+scheduled using a lower priority band in traffic control.
+
+The transfered bytes per connection can also be viewed through
+/proc/net/ip_conntrack and accessed via ctnetlink
+.TP
+[\fB!\fR]\fB --connbytes \fIfrom\fB:\fR[\fIto\fR]
+match packets from a connection whose packets/bytes/average packet
+size is more than FROM and less than TO bytes/packets. if TO is
+omitted only FROM check is done. "!" is used to match packets not
+falling in the range.
+.TP
+\fB--connbytes-dir\fR [\fBoriginal\fR|\fBreply\fR|\fBboth\fR]
+which packets to consider
+.TP
+\fB--connbytes-mode\fR [\fBpackets\fR|\fBbytes\fR|\fBavgpkt\fR]
+whether to check the amount of packets, number of bytes transferred or
+the average size (in bytes) of all packets received so far. Note that
+when "both" is used together with "avgpkt", and data is going (mainly)
+only in one direction (for example HTTP), the average packet size will
+be about half of the actual data packets.
+.TP
+Example:
+iptables .. -m connbytes --connbytes 10000:100000 --connbytes-dir both --connbytes-mode bytes ...
index c82c6e4..17b4d13 100644 (file)
@@ -26,14 +26,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -43,6 +35,7 @@ parse(int c, char **argv, int invert, unsigned int *flags,
       struct ipt_entry_match **match)
 {
        struct ipt_connlimit_info *info = (struct ipt_connlimit_info*)(*match)->data;
+       int i;
 
        if (0 == (*flags & 2)) {
                /* set default mask unless we've already seen a mask option */
@@ -58,7 +51,15 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                break;
 
        case '2':
-               info->mask = htonl(0xFFFFFFFF << (32 - atoi(argv[optind-1])));
+               i = atoi(argv[optind-1]);
+               if ((i < 0) || (i > 32))
+                       exit_error(PARAMETER_PROBLEM,
+                               "--connlimit-mask must be between 0 and 32");
+
+               if (i == 0)
+                       info->mask = 0;
+               else
+                       info->mask = htonl(0xFFFFFFFF << (32 - i));
                *flags |= 2;
                break;
 
@@ -113,17 +114,16 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 }
 
 static struct iptables_match connlimit = {
-       name:           "connlimit",
-       version:        IPTABLES_VERSION,
-       size:           IPT_ALIGN(sizeof(struct ipt_connlimit_info)),
-       userspacesize:  offsetof(struct ipt_connlimit_info,data),
-       help:           help,
-       init:           init,
-       parse:          parse,
-       final_check:    final_check,
-       print:          print,
-       save:           save,
-       extra_opts:     opts
+       .name           = "connlimit",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_connlimit_info)),
+       .userspacesize  = offsetof(struct ipt_connlimit_info,data),
+       .help           = help,
+       .parse          = parse,
+       .final_check    = final_check,
+       .print          = print,
+       .save           = save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_connlimit.man b/extensions/libipt_connlimit.man
new file mode 100644 (file)
index 0000000..404ee32
--- /dev/null
@@ -0,0 +1,21 @@
+Allows you to restrict the number of parallel TCP connections to a
+server per client IP address (or address block).
+.TP
+[\fB!\fR] \fB--connlimit-above \fIn\fR
+match if the number of existing tcp connections is (not) above n
+.TP
+.BI "--connlimit-mask " "bits"
+group hosts using mask
+.P
+Examples:
+.TP
+# allow 2 telnet connections per client host
+iptables -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT
+.TP
+# you can also match the other way around:
+iptables -p tcp --syn --dport 23 -m connlimit ! --connlimit-above 2 -j ACCEPT
+.TP
+# limit the nr of parallel http requests to 16 per class C sized \
+network (24 bit netmask)
+iptables -p tcp --syn --dport 80 -m connlimit --connlimit-above 16
+--connlimit-mask 24 -j REJECT
index 8f81f02..5bb2491 100644 (file)
@@ -1,4 +1,24 @@
-/* Shared library add-on to iptables to add CONNMARK matching support. */
+/* Shared library add-on to iptables to add connmark matching support.
+ *
+ * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * Version 1.1
+ *
+ * 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
+ */
 #include <stdio.h>
 #include <netdb.h>
 #include <string.h>
@@ -6,7 +26,7 @@
 #include <getopt.h>
 
 #include <iptables.h>
-#include <linux/netfilter_ipv4/ipt_connmark.h>
+#include "../include/linux/netfilter_ipv4/ipt_connmark.h"
 
 /* Function which prints out usage message. */
 static void
@@ -46,11 +66,17 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                char *end;
        case '1':
                check_inverse(optarg, &invert, &optind, 0);
+#ifdef KERNEL_64_USERSPACE_32
+               markinfo->mark = strtoull(optarg, &end, 0);
+               markinfo->mask = ~0ULL;
+               if (*end == '/')
+                       markinfo->mask = strtoull(end+1, &end, 0);
+#else
                markinfo->mark = strtoul(optarg, &end, 0);
-               if (*end == '/') {
+               markinfo->mask = ~0UL;
+               if (*end == '/')
                        markinfo->mask = strtoul(end+1, &end, 0);
-               } else
-                       markinfo->mask = 0xffffffff;
+#endif
                if (*end != '\0' || end == optarg)
                        exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
                if (invert)
@@ -64,14 +90,25 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        return 1;
 }
 
+#ifdef KERNEL_64_USERSPACE_32
+static void
+print_mark(unsigned long long mark, unsigned long long mask, int numeric)
+{
+       if(mask != ~0ULL)
+               printf("0x%llx/0x%llx ", mark, mask);
+       else
+               printf("0x%llx ", mark);
+}
+#else
 static void
 print_mark(unsigned long mark, unsigned long mask, int numeric)
 {
-       if(mask != 0xffffffff)
+       if(mask != ~0UL)
                printf("0x%lx/0x%lx ", mark, mask);
        else
                printf("0x%lx ", mark);
 }
+#endif
 
 /* Final check; must have specified --mark. */
 static void
@@ -96,7 +133,7 @@ print(const struct ipt_ip *ip,
        print_mark(info->mark, info->mask, numeric);
 }
 
-/* Saves the union ipt_matchinfo in parsable form to stdout. */
+/* Saves the matchinfo in parsable form to stdout. */
 static void
 save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 {
@@ -109,23 +146,21 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_mark(info->mark, info->mask, 0);
 }
 
-static
-struct iptables_match mark
-= { NULL,
-    "connmark",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_connmark_info)),
-    IPT_ALIGN(sizeof(struct ipt_connmark_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match connmark_match = {
+    .name          = "connmark",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_connmark_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_connmark_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
 };
 
 void _init(void)
 {
-       register_match(&mark);
+       register_match(&connmark_match);
 }
diff --git a/extensions/libipt_connmark.man b/extensions/libipt_connmark.man
new file mode 100644 (file)
index 0000000..a8e0600
--- /dev/null
@@ -0,0 +1,9 @@
+This module matches the netfilter mark field associated with a connection
+(which can be set using the
+.B CONNMARK
+target below).
+.TP
+.BI "--mark " "value[/mask]"
+Matches packets in connections with the given mark value (if a mask is
+specified, this is logically ANDed with the mark before the
+comparison).
diff --git a/extensions/libipt_connrate.c b/extensions/libipt_connrate.c
new file mode 100644 (file)
index 0000000..47c5fcb
--- /dev/null
@@ -0,0 +1,179 @@
+/* Shared library add-on to iptables to add connection rate tracking
+ * support.
+ *
+ * Copyright (c) 2004 Nuutti Kotivuori <naked@iki.fi>
+ *
+ * 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 <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_connrate.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"connrate v%s options:\n"
+" --connrate [!] [from]:[to]\n"
+"                              Match connection transfer rate in bytes\n"
+"                              per second. `inf' can be used for maximum\n"
+"                              expressible value.\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "connrate", 1, 0, '1' },
+       {0}
+};
+
+static u_int32_t
+parse_value(const char *arg, u_int32_t def)
+{
+       char *end;
+       size_t len;
+       u_int32_t value;
+
+       len = strlen(arg);
+       if(len == 0)
+               return def;
+       if(strcmp(arg, "inf") == 0)
+               return 0xFFFFFFFF;
+       value = strtoul(arg, &end, 0);
+       if(*end != '\0')
+               exit_error(PARAMETER_PROBLEM,
+                          "Bad value in range `%s'", arg);
+       return value;
+}
+
+static void
+parse_range(const char *arg, struct ipt_connrate_info *si)
+{
+       char *buffer;
+       char *colon;
+
+       buffer = strdup(arg);
+       if ((colon = strchr(buffer, ':')) == NULL)
+               exit_error(PARAMETER_PROBLEM, "Bad range `%s'", arg);
+       *colon = '\0';
+       si->from = parse_value(buffer, 0);
+       si->to = parse_value(colon+1, 0xFFFFFFFF);
+       if (si->from > si->to)
+               exit_error(PARAMETER_PROBLEM, "%u should be less than %u", si->from,si->to);
+       free(buffer);
+}
+
+#define CONNRATE_OPT 0x01
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_connrate_info *sinfo = (struct ipt_connrate_info *)(*match)->data;
+       u_int32_t tmp;
+
+       switch (c) {
+       case '1':
+               if (*flags & CONNRATE_OPT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--connrate' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_range(argv[optind-1], sinfo);
+               if (invert) {
+                       tmp = sinfo->from;
+                       sinfo->from = sinfo->to;
+                       sinfo->to = tmp;
+               }
+               *flags |= CONNRATE_OPT;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & CONNRATE_OPT))
+               exit_error(PARAMETER_PROBLEM,
+                          "connrate match: You must specify `--connrate'");
+}
+
+static void
+print_value(u_int32_t value)
+{
+       if(value == 0xFFFFFFFF)
+               printf("inf");
+       else
+               printf("%u", value);
+}
+
+static void
+print_range(struct ipt_connrate_info *sinfo)
+{
+       if (sinfo->from > sinfo->to) {
+               printf("! ");
+               print_value(sinfo->to);
+               printf(":");
+               print_value(sinfo->from);
+       } else {
+               print_value(sinfo->from);
+               printf(":");
+               print_value(sinfo->to);
+       }
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_connrate_info *sinfo = (struct ipt_connrate_info *)match->data;
+
+       printf("connrate ");
+       print_range(sinfo);
+       printf(" ");
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_connrate_info *sinfo = (struct ipt_connrate_info *)match->data;
+
+       printf("--connrate ");
+       print_range(sinfo);
+       printf(" ");
+}
+
+static struct iptables_match state = { 
+       .next           = NULL,
+       .name           = "connrate",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_connrate_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_connrate_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&state);
+}
diff --git a/extensions/libipt_connrate.man b/extensions/libipt_connrate.man
new file mode 100644 (file)
index 0000000..45cba9d
--- /dev/null
@@ -0,0 +1,6 @@
+This module matches the current transfer rate in a connection.
+.TP
+.BI "--connrate " "[!] [\fIfrom\fP]:[\fIto\fP]"
+Match against the current connection transfer rate being within 'from'
+and 'to' bytes per second. When the "!" argument is used before the
+range, the sense of the match is inverted.
index 63b38e9..5521684 100644 (file)
@@ -11,7 +11,8 @@
 #include <iptables.h>
 #include <linux/netfilter_ipv4/ip_conntrack.h>
 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#include <linux/netfilter_ipv4/ipt_conntrack.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_conntrack.h"
 
 #ifndef IPT_CONNTRACK_STATE_UNTRACKED
 #define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
@@ -55,14 +56,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static int
 parse_state(const char *state, size_t strlen, struct ipt_conntrack_info *sinfo)
 {
@@ -135,17 +128,29 @@ parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
                exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
 }
 
-
+#ifdef KERNEL_64_USERSPACE_32
+static unsigned long long
+parse_expire(const char *s)
+{
+       unsigned long long len;
+       
+       if (string_to_number_ll(s, 0, 0, &len) == -1)
+               exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
+       else
+               return len;
+}
+#else
 static unsigned long
 parse_expire(const char *s)
 {
        unsigned int len;
        
-       if (string_to_number(s, 0, 0xFFFFFFFF, &len) == -1)
+       if (string_to_number(s, 0, 0, &len) == -1)
                exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
        else
                return len;
 }
+#endif
 
 /* If a single value is provided, min and max are both set to the value */
 static void
@@ -162,15 +167,19 @@ parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
                cp++;
 
                sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
-               sinfo->expires_max = cp[0] ? parse_expire(cp) : 0xFFFFFFFF;
+               sinfo->expires_max = cp[0] ? parse_expire(cp) : -1;
        }
        free(buffer);
        
        if (sinfo->expires_min > sinfo->expires_max)
                exit_error(PARAMETER_PROBLEM,
+#ifdef KERNEL_64_USERSPACE_32
+                          "expire min. range value `%llu' greater than max. "
+                          "range value `%llu'", sinfo->expires_min, sinfo->expires_max);
+#else
                           "expire min. range value `%lu' greater than max. "
                           "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
-       
+#endif
 }
 
 /* Function which parses command options; returns true if it
@@ -475,7 +484,7 @@ matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, in
 
        if(sinfo->flags & IPT_CONNTRACK_STATUS) {
                printf("%sctstatus ", optpfx);
-               if (sinfo->invflags & IPT_CONNTRACK_STATE)
+               if (sinfo->invflags & IPT_CONNTRACK_STATUS)
                        printf("! ");
                print_status(sinfo->statusmask);
        }
@@ -485,10 +494,17 @@ matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, in
                if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
                        printf("! ");
 
+#ifdef KERNEL_64_USERSPACE_32
+               if (sinfo->expires_max == sinfo->expires_min)
+                       printf("%llu ", sinfo->expires_min);
+               else
+                       printf("%llu:%llu ", sinfo->expires_min, sinfo->expires_max);
+#else
                if (sinfo->expires_max == sinfo->expires_min)
                        printf("%lu ", sinfo->expires_min);
                else
                        printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
+#endif
        }
 }
 
@@ -504,23 +520,21 @@ print(const struct ipt_ip *ip,
 /* Saves the matchinfo in parsable form to stdout. */
 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 {
-       matchinfo_print(ip, match, 0, "--");
+       matchinfo_print(ip, match, 1, "--");
 }
 
-static
-struct iptables_match conntrack
-= { NULL,
-    "conntrack",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
-    IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match conntrack = { 
+       .next           = NULL,
+       .name           = "conntrack",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_conntrack.man b/extensions/libipt_conntrack.man
new file mode 100644 (file)
index 0000000..b732b28
--- /dev/null
@@ -0,0 +1,49 @@
+This module, when combined with connection tracking, allows access to
+more connection tracking information than the "state" match.
+(this module is present only if iptables was compiled under a kernel
+supporting this feature)
+.TP
+.BI "--ctstate " "state"
+Where state is a comma separated list of the connection states to
+match.  Possible states are
+.B INVALID
+meaning that the packet is associated with no known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
+.B SNAT
+A virtual state, matching if the original source address differs from
+the reply destination.
+.B DNAT
+A virtual state, matching if the original destination differs from the
+reply source.
+.TP
+.BI "--ctproto " "proto"
+Protocol to match (by number or name)
+.TP
+.BI "--ctorigsrc " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against original source address
+.TP
+.BI "--ctorigdst " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against original destination address
+.TP
+.BI "--ctreplsrc " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against reply source address
+.TP
+.BI "--ctrepldst " "[!] \fIaddress\fB[/\fImask\fP]"
+Match against reply destination address
+.TP
+.BI "--ctstatus " "[\fINONE|EXPECTED|SEEN_REPLY|ASSURED\fP][,...]"
+Match against internal conntrack states
+.TP
+.BI "--ctexpire " "\fItime\fP[\fI:time\fP]"
+Match remaining lifetime in seconds against given value
+or range of values (inclusive)
index 1d61da8..4520a6a 100644 (file)
 /* This is evil, but it's my code - HW*/
 #include "libipt_dscp_helper.c"
 
-static void init(struct ipt_entry_match *m, unsigned int *nfcache) 
-{
-       *nfcache |= NFC_IP_TOS;
-}
-
 static void help(void) 
 {
        printf(
@@ -157,20 +152,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_dscp(dinfo->dscp, dinfo->invert, 1);
 }
 
-static
-struct iptables_match dscp
-= { NULL,
-    "dscp",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_dscp_info)),
-    IPT_ALIGN(sizeof(struct ipt_dscp_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match dscp = { 
+       .next           = NULL,
+       .name           = "dscp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_dscp_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_dscp_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_dscp.man b/extensions/libipt_dscp.man
new file mode 100644 (file)
index 0000000..4a84210
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches the 6 bit DSCP field within the TOS field in the
+IP header.  DSCP has superseded TOS within the IETF.
+.TP
+.BI "--dscp " "value"
+Match against a numeric (decimal or hex) value [0-32].
+.TP
+.BI "--dscp-class " "\fIDiffServ Class\fP"
+Match the DiffServ class. This value may be any of the
+BE, EF, AFxx or CSx classes.  It will then be converted
+into it's according numeric value.
diff --git a/extensions/libipt_dstlimit.c b/extensions/libipt_dstlimit.c
new file mode 100644 (file)
index 0000000..3f3b633
--- /dev/null
@@ -0,0 +1,340 @@
+/* iptables match extension for limiting packets per destination
+ *
+ * (C) 2003 by Harald Welte <laforge@netfilter.org>
+ *
+ * Development of this code was funded by Astaro AG, http://www.astaro.com/
+ *
+ * Based on ipt_limit.c by
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <stddef.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_dstlimit.h>
+
+#define IPT_DSTLIMIT_BURST     5
+
+/* miliseconds */
+#define IPT_DSTLIMIT_GCINTERVAL        1000
+#define IPT_DSTLIMIT_EXPIRE    10000
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"dstlimit v%s options:\n"
+"--dstlimit <avg>              max average match rate\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--dstlimit-mode <mode>                mode\n"
+"                                      dstip\n"
+"                                      dstip-dstport\n"
+"                                      srcip-dstip\n"
+"                                      srcip-dstip-dstport\n"
+"--dstlimit-name <name>                name for /proc/net/ipt_dstlimit/\n"
+"[--dstlimit-burst <num>]      number to match in a burst, default %u\n"
+"[--dstlimit-htable-size <num>]        number of hashtable buckets\n"
+"[--dstlimit-htable-max <num>] number of hashtable entries\n"
+"[--dstlimit-htable-gcinterval]        interval between garbage collection runs\n"
+"[--dstlimit-htable-expire]    after which time are idle entries expired?\n"
+"\n", IPTABLES_VERSION, IPT_DSTLIMIT_BURST);
+}
+
+static struct option opts[] = {
+       { "dstlimit", 1, 0, '%' },
+       { "dstlimit-burst", 1, 0, '$' },
+       { "dstlimit-htable-size", 1, 0, '&' },
+       { "dstlimit-htable-max", 1, 0, '*' },
+       { "dstlimit-htable-gcinterval", 1, 0, '(' },
+       { "dstlimit-htable-expire", 1, 0, ')' },
+       { "dstlimit-mode", 1, 0, '_' },
+       { "dstlimit-name", 1, 0, '"' },
+       { 0 }
+};
+
+static
+int parse_rate(const char *rate, u_int32_t *val)
+{
+       const char *delim;
+       u_int32_t r;
+       u_int32_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 > IPT_DSTLIMIT_SCALE)
+               exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
+
+       *val = IPT_DSTLIMIT_SCALE * mult / r;
+       return 1;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_dstlimit_info *r = (struct ipt_dstlimit_info *)m->data;
+
+       r->cfg.burst = IPT_DSTLIMIT_BURST;
+       r->cfg.gc_interval = IPT_DSTLIMIT_GCINTERVAL;
+       r->cfg.expire = IPT_DSTLIMIT_EXPIRE;
+
+}
+
+#define PARAM_LIMIT            0x00000001
+#define PARAM_BURST            0x00000002
+#define PARAM_MODE             0x00000004
+#define PARAM_NAME             0x00000008
+#define PARAM_SIZE             0x00000010
+#define PARAM_MAX              0x00000020
+#define PARAM_GCINTERVAL       0x00000040
+#define PARAM_EXPIRE           0x00000080
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_dstlimit_info *r = 
+                       (struct ipt_dstlimit_info *)(*match)->data;
+       unsigned int num;
+
+       switch(c) {
+       case '%':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (!parse_rate(optarg, &r->cfg.avg))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad rate `%s'", optarg);
+               *flags |= PARAM_LIMIT;
+               break;
+
+       case '$':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 10000, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad --dstlimit-burst `%s'", optarg);
+               r->cfg.burst = num;
+               *flags |= PARAM_BURST;
+               break;
+       case '&':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --dstlimit-htable-size: `%s'", optarg);
+               r->cfg.size = num;
+               *flags |= PARAM_SIZE;
+               break;
+       case '*':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --dstlimit-htable-max: `%s'", optarg);
+               r->cfg.max = num;
+               *flags |= PARAM_MAX;
+               break;
+       case '(':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --dstlimit-htable-gcinterval: `%s'", 
+                               optarg);
+               /* FIXME: not HZ dependent!! */
+               r->cfg.gc_interval = num;
+               *flags |= PARAM_GCINTERVAL;
+               break;
+       case ')':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --dstlimit-htable-expire: `%s'", optarg);
+               /* FIXME: not HZ dependent */
+               r->cfg.expire = num;
+               *flags |= PARAM_EXPIRE;
+               break;
+       case '_':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (!strcmp(optarg, "dstip"))
+                       r->cfg.mode = IPT_DSTLIMIT_HASH_DIP;
+               else if (!strcmp(optarg, "dstip-destport") ||
+                        !strcmp(optarg, "dstip-dstport"))
+                       r->cfg.mode = IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT;
+               else if (!strcmp(optarg, "srcip-dstip"))
+                       r->cfg.mode = IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP;
+               else if (!strcmp(optarg, "srcip-dstip-destport") ||
+                        !strcmp(optarg, "srcip-dstip-dstport"))
+                       r->cfg.mode = IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT;
+               else
+                       exit_error(PARAMETER_PROBLEM, 
+                               "bad --dstlimit-mode: `%s'\n", optarg);
+               *flags |= PARAM_MODE;
+               break;
+       case '"':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (strlen(optarg) == 0)
+                       exit_error(PARAMETER_PROBLEM, "Zero-length name?");
+               strncpy(r->name, optarg, sizeof(r->name));
+               *flags |= PARAM_NAME;
+               break;
+       default:
+               return 0;
+       }
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "dstlimit does not support invert");
+
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+       if (!(flags & PARAM_LIMIT))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --dstlimit");
+       if (!(flags & PARAM_MODE))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --dstlimit-mode");
+       if (!(flags & PARAM_NAME))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --dstlimit-name");
+}
+
+static struct rates
+{
+       const char *name;
+       u_int32_t mult;
+} rates[] = { { "day", IPT_DSTLIMIT_SCALE*24*60*60 },
+             { "hour", IPT_DSTLIMIT_SCALE*60*60 },
+             { "min", IPT_DSTLIMIT_SCALE*60 },
+             { "sec", IPT_DSTLIMIT_SCALE } };
+
+static void print_rate(u_int32_t period)
+{
+       unsigned int i;
+
+       for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
+               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);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_dstlimit_info *r = 
+               (struct ipt_dstlimit_info *)match->data;
+       printf("limit: avg "); print_rate(r->cfg.avg);
+       printf("burst %u ", r->cfg.burst);
+       switch (r->cfg.mode) {
+               case (IPT_DSTLIMIT_HASH_DIP):
+                       printf("mode dstip ");
+                       break;
+               case (IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT):
+                       printf("mode dstip-dstport ");
+                       break;
+               case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP):
+                       printf("mode srcip-dstip ");
+                       break;
+               case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT):
+                       printf("mode srcip-dstip-dstport ");
+                       break;
+       }
+       if (r->cfg.size)
+               printf("htable-size %u ", r->cfg.size);
+       if (r->cfg.max)
+               printf("htable-max %u ", r->cfg.max);
+       if (r->cfg.gc_interval != IPT_DSTLIMIT_GCINTERVAL)
+               printf("htable-gcinterval %u ", r->cfg.gc_interval);
+       if (r->cfg.expire != IPT_DSTLIMIT_EXPIRE)
+               printf("htable-expire %u ", r->cfg.expire);
+}
+
+/* FIXME: Make minimalist: only print rate if not default --RR */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_dstlimit_info *r = 
+               (struct ipt_dstlimit_info *)match->data;
+
+       printf("--dstlimit "); print_rate(r->cfg.avg);
+       if (r->cfg.burst != IPT_DSTLIMIT_BURST)
+               printf("--dstlimit-burst %u ", r->cfg.burst);
+       switch (r->cfg.mode) {
+               case (IPT_DSTLIMIT_HASH_DIP):
+                       printf("--mode dstip ");
+                       break;
+               case (IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT):
+                       printf("--mode dstip-dstport ");
+                       break;
+               case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP):
+                       printf("--mode srcip-dstip ");
+                       break;
+               case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT):
+                       printf("--mode srcip-dstip-dstport ");
+                       break;
+       }
+       if (r->cfg.size)
+               printf("--dstlimit-htable-size %u ", r->cfg.size);
+       if (r->cfg.max)
+               printf("--dstlimit-htable-max %u ", r->cfg.max);
+       if (r->cfg.gc_interval != IPT_DSTLIMIT_GCINTERVAL)
+               printf("--dstlimit-htable-gcinterval %u", r->cfg.gc_interval);
+       if (r->cfg.expire != IPT_DSTLIMIT_EXPIRE)
+               printf("--dstlimit-htable-expire %u ", r->cfg.expire);
+}
+
+static struct iptables_match dstlimit = { 
+       .next           = NULL,
+       .name           = "dstlimit",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_dstlimit_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_dstlimit_info)),
+       //offsetof(struct ipt_dstlimit_info, prev),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&dstlimit);
+}
diff --git a/extensions/libipt_dstlimit.man b/extensions/libipt_dstlimit.man
new file mode 100644 (file)
index 0000000..e4a4a5a
--- /dev/null
@@ -0,0 +1,35 @@
+This module allows you to limit the packet per second (pps) rate on a per
+destination IP or per destination port base.  As opposed to the `limit' match,
+every destination ip / destination port has it's own limit.
+.TP
+.BI "--dstlimit " "avg"
+Maximum average match rate (packets per second unless followed by /sec /minute /hour /day postfixes).
+.TP
+.BI "--dstlimit-mode " "mode"
+The limiting hashmode.  Is the specified limit per
+.B dstip, dstip-dstport
+tuple, 
+.B srcip-dstip
+tuple, or per
+.B srcipdstip-dstport
+tuple.
+.TP
+.BI "--dstlimit-name " "name"
+Name for /proc/net/ipt_dstlimit/* file entry
+.TP
+.BI "[" "--dstlimit-burst " "burst" "]"
+Number of packets to match in a burst.  Default: 5
+.TP
+.BI "[" "--dstlimit-htable-size " "size" "]"
+Number of buckets in the hashtable
+.TP
+.BI "[" "--dstlimit-htable-max " "max" "]"
+Maximum number of entries in the hashtable
+.TP
+.BI "[" "--dstlimit-htable-gcinterval " "interval" "]"
+Interval between garbage collection runs of the hashtable (in miliseconds).
+Default is 1000 (1 second).
+.TP
+.BI "[" "--dstlimit-htable-expire " "time"
+After which time are idle entries expired from hashtable (in miliseconds)?
+Default is 10000 (10 seconds).
index d7b7f3b..97e839d 100644 (file)
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_ecn.h>
 
-static void init(struct ipt_entry_match *m, unsigned int *nfcache) 
-{
-       *nfcache |= NFC_IP_TOS;
-}
-
 static void help(void) 
 {
        printf(
@@ -163,7 +158,6 @@ struct iptables_match ecn
     .size          = IPT_ALIGN(sizeof(struct ipt_ecn_info)),
     .userspacesize = IPT_ALIGN(sizeof(struct ipt_ecn_info)),
     .help          = &help,
-    .init          = &init,
     .parse         = &parse,
     .final_check   = &final_check,
     .print         = &print,
diff --git a/extensions/libipt_ecn.man b/extensions/libipt_ecn.man
new file mode 100644 (file)
index 0000000..8ecfef5
--- /dev/null
@@ -0,0 +1,11 @@
+This allows you to match the ECN bits of the IPv4 and TCP header.  ECN is the Explicit Congestion Notification mechanism as specified in RFC3168
+.TP
+.BI "--ecn-tcp-cwr"
+This matches if the TCP ECN CWR (Congestion Window Received) bit is set.
+.TP
+.BI "--ecn-tcp-ece"
+This matches if the TCP ECN ECE (ECN Echo) bit is set.
+.TP
+.BI "--ecn-ip-ect " "num"
+This matches a particular IPv4 ECT (ECN-Capable Transport). You have to specify
+a number between `0' and `3'.
index 531fdd1..4abfba3 100644 (file)
@@ -168,20 +168,19 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 
 }
 
-static
-struct iptables_match esp
-= { NULL,
-    "esp",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_esp)),
-    IPT_ALIGN(sizeof(struct ipt_esp)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match esp = { 
+       .next           = NULL,
+       .name           = "esp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_esp)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_esp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void
diff --git a/extensions/libipt_esp.man b/extensions/libipt_esp.man
new file mode 100644 (file)
index 0000000..7b84368
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the SPIs in ESP header of IPSec packets.
+.TP
+.BR "--espspi " "[!] \fIspi\fP[:\fIspi\fP]"
index 6ebcb9f..d574db8 100644 (file)
@@ -43,7 +43,6 @@ static void
 init(struct ipt_entry_match *m, unsigned int *nfcache)
 {
        struct ipt_fuzzy_info *presentinfo = (struct ipt_fuzzy_info *)(m)->data;
-       *nfcache |= NFC_UNKNOWN;
 
        /*
         * Default rates ( I'll improve this very soon with something based 
@@ -138,19 +137,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 
 }
 
-struct iptables_match fuzzy_match
-= { NULL,
-    "fuzzy",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_fuzzy_info)),
-    IPT_ALIGN(sizeof(struct ipt_fuzzy_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match fuzzy_match = { 
+       .next           = NULL,
+       .name           = "fuzzy",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_fuzzy_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_fuzzy_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_fuzzy.man b/extensions/libipt_fuzzy.man
new file mode 100644 (file)
index 0000000..270c8d6
--- /dev/null
@@ -0,0 +1,7 @@
+This module matches a rate limit based on a fuzzy logic controller [FLC]
+.TP
+.BI "--lower-limit  "number"
+Specifies the lower limit (in packets per second).
+.TP
+.BI "--upper-limit " "number"
+Specifies the upper limit (in packets per second).
diff --git a/extensions/libipt_hashlimit.c b/extensions/libipt_hashlimit.c
new file mode 100644 (file)
index 0000000..6fb0ecc
--- /dev/null
@@ -0,0 +1,369 @@
+/* iptables match extension for limiting packets per destination
+ *
+ * (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
+ *
+ * Development of this code was funded by Astaro AG, http://www.astaro.com/
+ *
+ * Based on ipt_limit.c by
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ * 
+ * Error corections by nmalykh@bilim.com (22.01.2005)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <stddef.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_hashlimit.h>
+
+#define IPT_HASHLIMIT_BURST    5
+
+/* miliseconds */
+#define IPT_HASHLIMIT_GCINTERVAL       1000
+#define IPT_HASHLIMIT_EXPIRE   10000
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"hashlimit v%s options:\n"
+"--hashlimit <avg>             max average match rate\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--hashlimit-mode <mode>               mode is a comma-separated list of\n"
+"                                      dstip,srcip,dstport,srcport\n"
+"--hashlimit-name <name>               name for /proc/net/ipt_hashlimit/\n"
+"[--hashlimit-burst <num>]     number to match in a burst, default %u\n"
+"[--hashlimit-htable-size <num>]       number of hashtable buckets\n"
+"[--hashlimit-htable-max <num>]        number of hashtable entries\n"
+"[--hashlimit-htable-gcinterval]       interval between garbage collection runs\n"
+"[--hashlimit-htable-expire]   after which time are idle entries expired?\n"
+"\n", IPTABLES_VERSION, IPT_HASHLIMIT_BURST);
+}
+
+static struct option opts[] = {
+       { "hashlimit", 1, 0, '%' },
+       { "hashlimit-burst", 1, 0, '$' },
+       { "hashlimit-htable-size", 1, 0, '&' },
+       { "hashlimit-htable-max", 1, 0, '*' },
+       { "hashlimit-htable-gcinterval", 1, 0, '(' },
+       { "hashlimit-htable-expire", 1, 0, ')' },
+       { "hashlimit-mode", 1, 0, '_' },
+       { "hashlimit-name", 1, 0, '"' },
+       { 0 }
+};
+
+static
+int parse_rate(const char *rate, u_int32_t *val)
+{
+       const char *delim;
+       u_int32_t r;
+       u_int32_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 > IPT_HASHLIMIT_SCALE)
+               exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
+
+       *val = IPT_HASHLIMIT_SCALE * mult / r;
+       return 1;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_hashlimit_info *r = (struct ipt_hashlimit_info *)m->data;
+
+       r->cfg.burst = IPT_HASHLIMIT_BURST;
+       r->cfg.gc_interval = IPT_HASHLIMIT_GCINTERVAL;
+       r->cfg.expire = IPT_HASHLIMIT_EXPIRE;
+
+}
+
+
+/* Parse a 'mode' parameter into the required bitmask */
+static int parse_mode(struct ipt_hashlimit_info *r, char *optarg)
+{
+       char *tok;
+       char *arg = strdup(optarg);
+
+       if (!arg)
+               return -1;
+
+       r->cfg.mode = 0;
+
+       for (tok = strtok(arg, ",|");
+            tok;
+            tok = strtok(NULL, ",|")) {
+               if (!strcmp(tok, "dstip"))
+                       r->cfg.mode |= IPT_HASHLIMIT_HASH_DIP;
+               else if (!strcmp(tok, "srcip"))
+                       r->cfg.mode |= IPT_HASHLIMIT_HASH_SIP;
+               else if (!strcmp(tok, "srcport"))
+                       r->cfg.mode |= IPT_HASHLIMIT_HASH_SPT;
+               else if (!strcmp(tok, "dstport"))
+                       r->cfg.mode |= IPT_HASHLIMIT_HASH_DPT;
+               else {
+                       free(arg);
+                       return -1;
+               }
+       }
+       free(arg);
+       return 0;
+}
+
+#define PARAM_LIMIT            0x00000001
+#define PARAM_BURST            0x00000002
+#define PARAM_MODE             0x00000004
+#define PARAM_NAME             0x00000008
+#define PARAM_SIZE             0x00000010
+#define PARAM_MAX              0x00000020
+#define PARAM_GCINTERVAL       0x00000040
+#define PARAM_EXPIRE           0x00000080
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_hashlimit_info *r = 
+                       (struct ipt_hashlimit_info *)(*match)->data;
+       unsigned int num;
+
+       switch(c) {
+       case '%':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (!parse_rate(optarg, &r->cfg.avg))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad rate `%s'", optarg);
+               *flags |= PARAM_LIMIT;
+               break;
+
+       case '$':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 10000, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad --hashlimit-burst `%s'", optarg);
+               r->cfg.burst = num;
+               *flags |= PARAM_BURST;
+               break;
+       case '&':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-size: `%s'", optarg);
+               r->cfg.size = num;
+               *flags |= PARAM_SIZE;
+               break;
+       case '*':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-max: `%s'", optarg);
+               r->cfg.max = num;
+               *flags |= PARAM_MAX;
+               break;
+       case '(':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-gcinterval: `%s'", 
+                               optarg);
+               /* FIXME: not HZ dependent!! */
+               r->cfg.gc_interval = num;
+               *flags |= PARAM_GCINTERVAL;
+               break;
+       case ')':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-expire: `%s'", optarg);
+               /* FIXME: not HZ dependent */
+               r->cfg.expire = num;
+               *flags |= PARAM_EXPIRE;
+               break;
+       case '_':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (parse_mode(r, optarg) < 0)
+                       exit_error(PARAMETER_PROBLEM, 
+                                  "bad --hashlimit-mode: `%s'\n", optarg);
+               *flags |= PARAM_MODE;
+               break;
+       case '"':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (strlen(optarg) == 0)
+                       exit_error(PARAMETER_PROBLEM, "Zero-length name?");
+               strncpy(r->name, optarg, sizeof(r->name));
+               *flags |= PARAM_NAME;
+               break;
+       default:
+               return 0;
+       }
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "hashlimit does not support invert");
+
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+       if (!(flags & PARAM_LIMIT))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit");
+       if (!(flags & PARAM_MODE))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit-mode");
+       if (!(flags & PARAM_NAME))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit-name");
+}
+
+static struct rates
+{
+       const char *name;
+       u_int32_t mult;
+} rates[] = { { "day", IPT_HASHLIMIT_SCALE*24*60*60 },
+             { "hour", IPT_HASHLIMIT_SCALE*60*60 },
+             { "min", IPT_HASHLIMIT_SCALE*60 },
+             { "sec", IPT_HASHLIMIT_SCALE } };
+
+static void print_rate(u_int32_t period)
+{
+       unsigned int i;
+
+       for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
+               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);
+}
+
+static void print_mode(const struct ipt_hashlimit_info *r, char separator)
+{
+       int prevmode = 0;
+
+       if (r->cfg.mode & IPT_HASHLIMIT_HASH_SIP) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("srcip", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & IPT_HASHLIMIT_HASH_SPT) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("srcport", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & IPT_HASHLIMIT_HASH_DIP) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("dstip", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & IPT_HASHLIMIT_HASH_DPT) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("dstport", stdout);
+       }
+       putchar(' ');
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_hashlimit_info *r = 
+               (struct ipt_hashlimit_info *)match->data;
+       fputs("limit: avg ", stdout); print_rate(r->cfg.avg);
+       printf("burst %u ", r->cfg.burst);
+       fputs("mode ", stdout);
+       print_mode(r, '-');
+       if (r->cfg.size)
+               printf("htable-size %u ", r->cfg.size);
+       if (r->cfg.max)
+               printf("htable-max %u ", r->cfg.max);
+       if (r->cfg.gc_interval != IPT_HASHLIMIT_GCINTERVAL)
+               printf("htable-gcinterval %u ", r->cfg.gc_interval);
+       if (r->cfg.expire != IPT_HASHLIMIT_EXPIRE)
+               printf("htable-expire %u ", r->cfg.expire);
+}
+
+/* FIXME: Make minimalist: only print rate if not default --RR */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_hashlimit_info *r = 
+               (struct ipt_hashlimit_info *)match->data;
+
+       fputs("--hashlimit ", stdout); print_rate(r->cfg.avg);
+       if (r->cfg.burst != IPT_HASHLIMIT_BURST)
+               printf("--hashlimit-burst %u ", r->cfg.burst);
+
+       fputs("--hashlimit-mode ", stdout);
+       print_mode(r, ',');
+       
+       printf("--hashlimit-name %s ", r->name);
+
+       if (r->cfg.size)
+               printf("--hashlimit-htable-size %u ", r->cfg.size);
+       if (r->cfg.max)
+               printf("--hashlimit-htable-max %u ", r->cfg.max);
+       if (r->cfg.gc_interval != IPT_HASHLIMIT_GCINTERVAL)
+               printf("--hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
+       if (r->cfg.expire != IPT_HASHLIMIT_EXPIRE)
+               printf("--hashlimit-htable-expire %u ", r->cfg.expire);
+}
+
+static struct iptables_match hashlimit = { NULL,
+       .name           = "hashlimit",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_hashlimit_info)),
+       .userspacesize  = offsetof(struct ipt_hashlimit_info, hinfo),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&hashlimit);
+}
diff --git a/extensions/libipt_hashlimit.man b/extensions/libipt_hashlimit.man
new file mode 100644 (file)
index 0000000..1b0a5d4
--- /dev/null
@@ -0,0 +1,35 @@
+This patch adds a new match called 'hashlimit'.
+The idea is to have something like 'limit', but either per
+destination-ip or per (destip,destport) tuple.
+
+It gives you the ability to express
+.IP
+ '1000 packets per second for every host in 192.168.0.0/16'
+.IP
+ '100 packets per second for every service of 192.168.1.1'
+.P
+with a single iptables rule.
+.TP
+.BI "--hashlimit " "rate"
+A rate just like the limit match
+.TP
+.BI "--hashlimit-burst " "num"
+Burst value, just like limit match
+.TP
+.BI "--hashlimit-mode " "destip | destip-destport"
+Limit per IP or per port
+.TP
+.BI "--hashlimit-name " "foo"
+The name for the /proc/net/ipt_hashlimit/foo entry
+.TP
+.BI "--hashlimit-htable-size " "num"
+The number of buckets of the hash table
+.TP
+.BI "--hashlimit-htable-max " "num"
+Maximum entries in the hash
+.TP
+.BI "--hashlimit-htable-expire " "num"
+After how many miliseconds do hash entries expire
+.TP
+.BI "--hashlimit-htable-gcinterval " "num"
+How many miliseconds between garbage collection intervals
index 4b16e02..7c9f3e3 100644 (file)
@@ -24,14 +24,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this. */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -44,8 +36,12 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch (c) {
        case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                       "helper match: Only use --helper ONCE!");
                check_inverse(optarg, &invert, &invert, 0);
                strncpy(info->name, optarg, 29);
+               info->name[29] = '\0';
                if (invert)
                        info->invert = 1;
                *flags = 1;
@@ -86,20 +82,17 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        printf("%s--helper \"%s\" ",info->invert ? "! " : "", info->name);
 }
 
-static
-struct iptables_match helper
-= { NULL,
-    "helper",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_helper_info)),
-    IPT_ALIGN(sizeof(struct ipt_helper_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match helper = { 
+       .next           = NULL,
+       .name           = "helper",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_helper_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_helper.man b/extensions/libipt_helper.man
new file mode 100644 (file)
index 0000000..c3221ad
--- /dev/null
@@ -0,0 +1,11 @@
+This module matches packets related to a specific conntrack-helper.
+.TP
+.BI "--helper " "string"
+Matches packets related to the specified conntrack-helper.
+.RS
+.PP
+string can be "ftp" for packets related to a ftp-session on default port.
+For other ports append -portnr to the value, ie. "ftp-2121".
+.PP
+Same rules apply for other conntrack-helpers.
+.RE
index f1d3dcd..9d45c8c 100644 (file)
@@ -114,7 +114,7 @@ static struct option opts[] = {
        {0}
 };
 
-static unsigned int
+static void 
 parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[])
 {
        unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names);
@@ -165,10 +165,6 @@ parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[])
                        code[1] = 0xFF;
                }
        }
-
-       if (code[0] == 0 && code[1] == 0xFF)
-               return NFC_IP_SRC_PT;
-       else return NFC_IP_SRC_PT | NFC_IP_DST_PT;
 }
 
 /* Initialize the match. */
@@ -194,9 +190,8 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        switch (c) {
        case '1':
                check_inverse(optarg, &invert, &optind, 0);
-               *nfcache |= parse_icmp(argv[optind-1],
-                                      &icmpinfo->type,
-                                      icmpinfo->code);
+               parse_icmp(argv[optind-1], &icmpinfo->type, 
+                          icmpinfo->code);
                if (invert)
                        icmpinfo->invflags |= IPT_ICMP_INV;
                break;
@@ -271,10 +266,15 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        if (icmp->invflags & IPT_ICMP_INV)
                printf("! ");
 
-       printf("--icmp-type %u", icmp->type);
-       if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
-               printf("/%u", icmp->code[0]);
-       printf(" ");
+       /* special hack for 'any' case */
+       if (icmp->type == 0xFF) {
+               printf("--icmp-type any ");
+       } else {
+               printf("--icmp-type %u", icmp->type);
+               if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
+                       printf("/%u", icmp->code[0]);
+               printf(" ");
+       }
 }
 
 /* Final check; we don't care. */
@@ -282,20 +282,19 @@ static void final_check(unsigned int flags)
 {
 }
 
-static
-struct iptables_match icmp
-= { NULL,
-    "icmp",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_icmp)),
-    IPT_ALIGN(sizeof(struct ipt_icmp)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match icmp = { 
+       .next           = NULL,
+       .name           = "icmp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_icmp)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_icmp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_icmp.man b/extensions/libipt_icmp.man
new file mode 100644 (file)
index 0000000..5b91514
--- /dev/null
@@ -0,0 +1,9 @@
+This extension is loaded if `--protocol icmp' is specified.  It
+provides the following option:
+.TP
+.BR "--icmp-type " "[!] \fItypename\fP"
+This allows specification of the ICMP type, which can be a numeric
+ICMP type, or one of the ICMP type names shown by the command
+.nf
+ iptables -p icmp -h
+.fi
index 8445884..2ada8e2 100644 (file)
@@ -26,14 +26,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this. */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static void
 parse_iprange(char *arg, struct ipt_iprange *range)
 {
@@ -99,7 +91,7 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                        info->flags |= IPRANGE_DST_INV;
 
                parse_iprange(optarg, &info->dst);              
-               *flags = 1;
+
                break;
 
        default:
@@ -173,20 +165,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        }
 }
 
-static
-struct iptables_match iprange
-= { NULL,
-    "iprange",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_iprange_info)),
-    IPT_ALIGN(sizeof(struct ipt_iprange_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match iprange = { 
+       .next           = NULL,
+       .name           = "iprange",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_iprange_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_iprange_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_iprange.man b/extensions/libipt_iprange.man
new file mode 100644 (file)
index 0000000..57e1cff
--- /dev/null
@@ -0,0 +1,7 @@
+This matches on a given arbitrary range of IPv4 addresses
+.TP
+.BI "[!]" "--src-range " "ip-ip"
+Match source IP in the specified range.
+.TP
+.BI "[!]" "--dst-range " "ip-ip"
+Match destination IP in the specified range.
index 2aec10b..3d3b236 100644 (file)
@@ -35,14 +35,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* caching not yet implemented */
-        *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -299,20 +291,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        printf(" ");
 }
 
-static
-struct iptables_match ipv4options_struct
-= { NULL,
-    "ipv4options",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_ipv4options_info)),
-    IPT_ALIGN(sizeof(struct ipt_ipv4options_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match ipv4options_struct = { 
+       .next           = NULL,
+       .name           = "ipv4options",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ipv4options_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ipv4options_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_ipv4options.man b/extensions/libipt_ipv4options.man
new file mode 100644 (file)
index 0000000..122dc68
--- /dev/null
@@ -0,0 +1,32 @@
+Match on IPv4 header options like source routing, record route,
+timestamp and router-alert.
+.TP
+.B "--ssrr"
+To match packets with the flag strict source routing.
+.TP
+.B "--lsrr"
+To match packets with the flag loose source routing.
+.TP
+.B "--no-srr"
+To match packets with no flag for source routing.
+.TP
+.B "\fR[\fB!\fR]\fB --rr"
+To match packets with the RR flag.
+.TP
+.B "\fR[\fB!\fR]\fB --ts"
+To match packets with the TS flag.
+.TP
+.B "\fR[\fB!\fR]\fB --ra"
+To match packets with the router-alert option.
+.TP
+.B "\fR[\fB!\fR]\fB --any-opt"
+To match a packet with at least one IP option, or no IP option
+at all if ! is chosen.
+.TP
+Examples:
+.TP
+$ iptables -A input -m ipv4options --rr -j DROP
+will drop packets with the record-route flag.
+.TP
+$ iptables -A input -m ipv4options --ts -j DROP
+will drop packets with the timestamp flag.
index 8ac98b6..cfac1c5 100644 (file)
@@ -25,13 +25,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static u_int16_t
 parse_length(const char *s)
 {
@@ -138,20 +131,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_length((struct ipt_length_info *)match->data);
 }
 
-static
-struct iptables_match length
-= { NULL,
-    "length",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_length_info)),
-    IPT_ALIGN(sizeof(struct ipt_length_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match length = { 
+       .next           = NULL,
+       .name           = "length",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_length_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_length_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_length.man b/extensions/libipt_length.man
new file mode 100644 (file)
index 0000000..72a6b5d
--- /dev/null
@@ -0,0 +1,4 @@
+This module matches the length of a packet against a specific value
+or range of values.
+.TP
+.BR "--length " "\fIlength\fP[:\fIlength\fP]"
index af381fa..7f0337a 100644 (file)
@@ -11,7 +11,8 @@
 #include <iptables.h>
 #include <stddef.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ipt_limit.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_limit.h"
 
 #define IPT_LIMIT_AVG  "3/hour"
 #define IPT_LIMIT_BURST        5
@@ -80,8 +81,6 @@ init(struct ipt_entry_match *m, unsigned int *nfcache)
        parse_rate(IPT_LIMIT_AVG, &r->avg);
        r->burst = IPT_LIMIT_BURST;
 
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 /* FIXME: handle overflow:
@@ -103,19 +102,14 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch(c) {
        case '%':
-               if (check_inverse(optarg, &invert, NULL, 0))
-                       exit_error(PARAMETER_PROBLEM,
-                                  "Unexpected `!' after --limit");
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
                if (!parse_rate(optarg, &r->avg))
                        exit_error(PARAMETER_PROBLEM,
                                   "bad rate `%s'", optarg);
                break;
 
        case '$':
-               if (check_inverse(optarg, &invert, NULL, 0))
-                       exit_error(PARAMETER_PROBLEM,
-                                  "Unexpected `!' after --limit-burst");
-
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
                if (string_to_number(optarg, 0, 10000, &num) == -1)
                        exit_error(PARAMETER_PROBLEM,
                                   "bad --limit-burst `%s'", optarg);
@@ -126,6 +120,10 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                return 0;
        }
 
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "limit does not support invert");
+
        return 1;
 }
 
@@ -177,20 +175,19 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
                printf("--limit-burst %u ", r->burst);
 }
 
-static
-struct iptables_match limit
-= { NULL,
-    "limit",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_rateinfo)),
-    offsetof(struct ipt_rateinfo, prev),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match limit = { 
+       .next           = NULL,
+       .name           = "limit",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_rateinfo)),
+       .userspacesize  = offsetof(struct ipt_rateinfo, prev),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_limit.man b/extensions/libipt_limit.man
new file mode 100644 (file)
index 0000000..84b63d4
--- /dev/null
@@ -0,0 +1,15 @@
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached
+(unless the `!' flag is used).  It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+Maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
index d1079a5..bac8512 100644 (file)
@@ -28,14 +28,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static void
 parse_mac(const char *mac, struct ipt_mac_info *info)
 {
@@ -128,20 +120,18 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_mac(((struct ipt_mac_info *)match->data)->srcaddr);
 }
 
-static
-struct iptables_match mac
-= { NULL,
-    "mac",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_mac_info)),
-    IPT_ALIGN(sizeof(struct ipt_mac_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match mac = { 
+       .next           = NULL,
+       .name           = "mac",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mac_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mac_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_mac.man b/extensions/libipt_mac.man
new file mode 100644 (file)
index 0000000..5321ca1
--- /dev/null
@@ -0,0 +1,10 @@
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address.  It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
index 4aa780b..1922768 100644 (file)
@@ -6,7 +6,8 @@
 #include <getopt.h>
 
 #include <iptables.h>
-#include <linux/netfilter_ipv4/ipt_mark.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_mark.h"
 
 /* Function which prints out usage message. */
 static void
@@ -24,14 +25,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this. */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -46,11 +39,19 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                char *end;
        case '1':
                check_inverse(optarg, &invert, &optind, 0);
+#ifdef KERNEL_64_USERSPACE_32
+               markinfo->mark = strtoull(optarg, &end, 0);
+               if (*end == '/') {
+                       markinfo->mask = strtoull(end+1, &end, 0);
+               } else
+                       markinfo->mask = 0xffffffffffffffffULL;
+#else
                markinfo->mark = strtoul(optarg, &end, 0);
                if (*end == '/') {
                        markinfo->mask = strtoul(end+1, &end, 0);
                } else
                        markinfo->mask = 0xffffffff;
+#endif
                if (*end != '\0' || end == optarg)
                        exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
                if (invert)
@@ -64,6 +65,16 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        return 1;
 }
 
+#ifdef KERNEL_64_USERSPACE_32
+static void
+print_mark(unsigned long long mark, unsigned long long mask, int numeric)
+{
+       if(mask != 0xffffffffffffffffULL)
+               printf("0x%llx/0x%llx ", mark, mask);
+       else
+               printf("0x%llx ", mark);
+}
+#else
 static void
 print_mark(unsigned long mark, unsigned long mask, int numeric)
 {
@@ -72,6 +83,7 @@ print_mark(unsigned long mark, unsigned long mask, int numeric)
        else
                printf("0x%lx ", mark);
 }
+#endif
 
 /* Final check; must have specified --mark. */
 static void
@@ -111,20 +123,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_mark(info->mark, info->mask, 0);
 }
 
-static
-struct iptables_match mark
-= { NULL,
-    "mark",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_mark_info)),
-    IPT_ALIGN(sizeof(struct ipt_mark_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match mark = { 
+       .next           = NULL,
+       .name           = "mark",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mark_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mark_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_mark.man b/extensions/libipt_mark.man
new file mode 100644 (file)
index 0000000..05f8e1e
--- /dev/null
@@ -0,0 +1,9 @@
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BR "--mark " "\fIvalue\fP[/\fImask\fP]"
+Matches packets with the given unsigned mark value (if a mask is
+specified, this is logically ANDed with the mask before the
+comparison).
index 2ae61ff..a387b21 100644 (file)
@@ -136,30 +136,34 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch (c) {
        case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                parse_multi_ports(argv[optind-1], minfo, proto);
                minfo->flags = IPT_MPORT_SOURCE;
-               *nfcache |= NFC_IP_SRC_PT;
                break;
 
        case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                parse_multi_ports(argv[optind-1], minfo, proto);
                minfo->flags = IPT_MPORT_DESTINATION;
-               *nfcache |= NFC_IP_DST_PT;
                break;
 
        case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                parse_multi_ports(argv[optind-1], minfo, proto);
                minfo->flags = IPT_MPORT_EITHER;
-               *nfcache |= NFC_IP_SRC_PT | NFC_IP_DST_PT;
                break;
 
        default:
                return 0;
        }
 
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport does not support invert");
+
        if (*flags)
                exit_error(PARAMETER_PROBLEM,
                           "multiport can only have one option");
@@ -285,19 +289,19 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        printf(" ");
 }
 
-struct iptables_match mport
-= { NULL,
-    "mport",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_mport)),
-    IPT_ALIGN(sizeof(struct ipt_mport)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match mport = { 
+       .next           = NULL,
+       .name           = "mport",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mport)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mport)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void
diff --git a/extensions/libipt_mport.man b/extensions/libipt_mport.man
new file mode 100644 (file)
index 0000000..cead84e
--- /dev/null
@@ -0,0 +1,19 @@
+This module matches a set of source or destination ports.  Up to 15
+ports can be specified.  It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the source port is one of the given ports.  The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the destination port is one of the given ports.  The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the both the source and destination ports are equal to each
+other and to one of the given ports.
index c9a98b3..9f5193f 100644 (file)
@@ -5,7 +5,8 @@
 #include <stdlib.h>
 #include <getopt.h>
 #include <iptables.h>
-#include <linux/netfilter_ipv4/ipt_multiport.h>
+/* To ensure that iptables compiles with an old kernel */
+#include "../include/linux/netfilter_ipv4/ipt_multiport.h"
 
 /* Function which prints out usage message. */
 static void
@@ -20,6 +21,23 @@ help(void)
 " --dports ...\n"
 "                              match destination port(s)\n"
 " --ports port[,port,port]\n"
+"                              match both source and destination port(s)\n"
+" NOTE: this kernel does not support port ranges in multiport.\n",
+IPTABLES_VERSION);
+}
+
+static void
+help_v1(void)
+{
+       printf(
+"multiport v%s options:\n"
+" --source-ports [!] port[,port:port,port...]\n"
+" --sports ...\n"
+"                              match source port(s)\n"
+" --destination-ports [!] port[,port:port,port...]\n"
+" --dports ...\n"
+"                              match destination port(s)\n"
+" --ports [!] port[,port:port,port]\n"
 "                              match both source and destination port(s)\n",
 IPTABLES_VERSION);
 }
@@ -77,6 +95,46 @@ parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
        return i;
 }
 
+static void
+parse_multi_ports_v1(const char *portstring, 
+                    struct ipt_multiport_v1 *multiinfo,
+                    const char *proto)
+{
+       char *buffer, *cp, *next, *range;
+       unsigned int i;
+       u_int16_t m;
+
+       buffer = strdup(portstring);
+       if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+       for (i=0; i<IPT_MULTI_PORTS; i++)
+               multiinfo->pflags[i] = 0;
+       for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next, i++) {
+               next=strchr(cp, ',');
+               if (next) *next++='\0';
+               range = strchr(cp, ':');
+               if (range) {
+                       if (i == IPT_MULTI_PORTS-1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "too many ports specified");
+                       *range++ = '\0';
+               }
+               multiinfo->ports[i] = parse_port(cp, proto);
+               if (range) {
+                       multiinfo->pflags[i] = 1;
+                       multiinfo->ports[++i] = parse_port(range, proto);
+                       if (multiinfo->ports[i-1] >= multiinfo->ports[i])
+                               exit_error(PARAMETER_PROBLEM,
+                                          "invalid portrange specified");
+                       m <<= 1;
+               }
+       }
+       multiinfo->count = i;
+       if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
+       free(buffer);
+}
+
 /* Initialize the match. */
 static void
 init(struct ipt_entry_match *m, unsigned int *nfcache)
@@ -86,6 +144,10 @@ init(struct ipt_entry_match *m, unsigned int *nfcache)
 static const char *
 check_proto(const struct ipt_entry *entry)
 {
+       if (entry->ip.invflags & IPT_INV_PROTO)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport only works with TCP or UDP");
+
        if (entry->ip.proto == IPPROTO_TCP)
                return "tcp";
        else if (entry->ip.proto == IPPROTO_UDP)
@@ -112,33 +174,83 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch (c) {
        case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                multiinfo->count = parse_multi_ports(argv[optind-1],
                                                     multiinfo->ports, proto);
                multiinfo->flags = IPT_MULTIPORT_SOURCE;
-               *nfcache |= NFC_IP_SRC_PT;
                break;
 
        case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                multiinfo->count = parse_multi_ports(argv[optind-1],
                                                     multiinfo->ports, proto);
                multiinfo->flags = IPT_MULTIPORT_DESTINATION;
-               *nfcache |= NFC_IP_DST_PT;
                break;
 
        case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
                proto = check_proto(entry);
                multiinfo->count = parse_multi_ports(argv[optind-1],
                                                     multiinfo->ports, proto);
                multiinfo->flags = IPT_MULTIPORT_EITHER;
-               *nfcache |= NFC_IP_SRC_PT | NFC_IP_DST_PT;
                break;
 
        default:
                return 0;
        }
 
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport does not support invert");
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport can only have one option");
+       *flags = 1;
+       return 1;
+}
+
+static int
+parse_v1(int c, char **argv, int invert, unsigned int *flags,
+        const struct ipt_entry *entry,
+        unsigned int *nfcache,
+        struct ipt_entry_match **match)
+{
+       const char *proto;
+       struct ipt_multiport_v1 *multiinfo
+               = (struct ipt_multiport_v1 *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IPT_MULTIPORT_SOURCE;
+               break;
+
+       case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IPT_MULTIPORT_DESTINATION;
+               break;
+
+       case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IPT_MULTIPORT_EITHER;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (invert)
+               multiinfo->invert = 1;
+
        if (*flags)
                exit_error(PARAMETER_PROBLEM,
                           "multiport can only have one option");
@@ -214,6 +326,49 @@ print(const struct ipt_ip *ip,
        printf(" ");
 }
 
+static void
+print_v1(const struct ipt_ip *ip,
+        const struct ipt_entry_match *match,
+        int numeric)
+{
+       const struct ipt_multiport_v1 *multiinfo
+               = (const struct ipt_multiport_v1 *)match->data;
+       unsigned int i;
+
+       printf("multiport ");
+
+       switch (multiinfo->flags) {
+       case IPT_MULTIPORT_SOURCE:
+               printf("sports ");
+               break;
+
+       case IPT_MULTIPORT_DESTINATION:
+               printf("dports ");
+               break;
+
+       case IPT_MULTIPORT_EITHER:
+               printf("ports ");
+               break;
+
+       default:
+               printf("ERROR ");
+               break;
+       }
+
+       if (multiinfo->invert)
+               printf("! ");
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, numeric);
+               if (multiinfo->pflags[i]) {
+                       printf(":");
+                       print_port(multiinfo->ports[++i], ip->proto, numeric);
+               }
+       }
+       printf(" ");
+}
+
 /* Saves the union ipt_matchinfo in parsable form to stdout. */
 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 {
@@ -242,24 +397,76 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        printf(" ");
 }
 
-static
-struct iptables_match multiport
-= { NULL,
-    "multiport",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_multiport)),
-    IPT_ALIGN(sizeof(struct ipt_multiport)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static void save_v1(const struct ipt_ip *ip, 
+                   const struct ipt_entry_match *match)
+{
+       const struct ipt_multiport_v1 *multiinfo
+               = (const struct ipt_multiport_v1 *)match->data;
+       unsigned int i;
+
+       switch (multiinfo->flags) {
+       case IPT_MULTIPORT_SOURCE:
+               printf("--sports ");
+               break;
+
+       case IPT_MULTIPORT_DESTINATION:
+               printf("--dports ");
+               break;
+
+       case IPT_MULTIPORT_EITHER:
+               printf("--ports ");
+               break;
+       }
+
+       if (multiinfo->invert)
+               printf("! ");
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, 1);
+               if (multiinfo->pflags[i]) {
+                       printf(":");
+                       print_port(multiinfo->ports[++i], ip->proto, 1);
+               }
+       }
+       printf(" ");
+}
+
+static struct iptables_match multiport = { 
+       .next           = NULL,
+       .name           = "multiport",
+       .revision       = 0,
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_multiport)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_multiport)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+static struct iptables_match multiport_v1 = { 
+       .next           = NULL,
+       .name           = "multiport",
+       .version        = IPTABLES_VERSION,
+       .revision       = 1,
+       .size           = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
+       .help           = &help_v1,
+       .init           = &init,
+       .parse          = &parse_v1,
+       .final_check    = &final_check,
+       .print          = &print_v1,
+       .save           = &save_v1,
+       .extra_opts     = opts
 };
 
 void
 _init(void)
 {
        register_match(&multiport);
+       register_match(&multiport_v1);
 }
diff --git a/extensions/libipt_multiport.man b/extensions/libipt_multiport.man
new file mode 100644 (file)
index 0000000..ba760e9
--- /dev/null
@@ -0,0 +1,20 @@
+This module matches a set of source or destination ports.  Up to 15
+ports can be specified.  A port range (port:port) counts as two
+ports.  It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the source port is one of the given ports.  The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the destination port is one of the given ports.  The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if either the source or destination ports are equal to one of
+the given ports.
index ab8f97d..6f483b9 100644 (file)
@@ -30,10 +30,10 @@ help(void)
        printf(
 "nth v%s options:\n"
 "   --every     Nth              Match every Nth packet\n"
-"  [--counter]  num              Use counter 0-%u (default:0)\n"
-"  [--start]    num              Initialize the counter at the number 'num'\n"
+"  [--counter   num ]            Use counter 0-%u (default:0)\n"
+"  [--start     num ]            Initialize the counter at the number 'num'\n"
 "                                instead of 0. Must be between 0 and Nth-1\n"
-"  [--packet]   num              Match on 'num' packet. Must be between 0\n"
+"  [--packet    num ]            Match on 'num' packet. Must be between 0\n"
 "                                and Nth-1.\n\n"
 "                                If --packet is used for a counter than\n"
 "                                there must be Nth number of --packet\n"
@@ -50,13 +50,6 @@ static struct option opts[] = {
        { 0 }
 };
 
-/* Initialize the target. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
 #define IPT_NTH_OPT_EVERY      0x01
 #define IPT_NTH_OPT_NOT_EVERY  0x02
 #define IPT_NTH_OPT_START      0x04
@@ -217,19 +210,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
                 printf("--packet %u ", nthinfo->packet );
 }
 
-struct iptables_match nth
-= { NULL,
-    "nth",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_nth_info)),
-    IPT_ALIGN(sizeof(struct ipt_nth_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match nth = { 
+       .next           = NULL,
+       .name           = "nth",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_nth_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_nth_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_nth.man b/extensions/libipt_nth.man
new file mode 100644 (file)
index 0000000..d215fd5
--- /dev/null
@@ -0,0 +1,14 @@
+This module matches every `n'th packet
+.TP
+.BI "--every " "value"
+Match every `value' packet
+.TP
+.BI "[" "--counter " "num" "]"
+Use internal counter number `num'.  Default is `0'.
+.TP
+.BI "[" "--start " "num" "]"
+Initialize the counter at the number `num' insetad of `0'.  Most between `0'
+and `value'-1.
+.TP
+.BI "[" "--packet " "num" "]"
+Match on `num' packet.  Most be between `0' and `value'-1.
index 205f071..6747404 100644 (file)
 
 static void help(void)
 {
-       printf("OS fingerprint match v%s options:\n"
-               "  --genre [!] string          Match a OS genre bypassive fingerprinting.\n",
-               IPTABLES_VERSION);
+       printf("OS fingerprint match options:\n"
+               "--genre [!] string     Match a OS genre by passive fingerprinting.\n"
+               "--smart                Use some smart extensions to determine OS (do not use TTL).\n"
+               "--log level            Log all(or only first) determined genres even if "
+                                       "they do not match desired one. "
+                                       "Level may be 0(all) or 1(only first entry).\n"
+               "--netlink              Log through netlink(NETLINK_NFLOG).\n",
+               "--connector            Log through kernel connector [in 2.6.12-mm+].\n"
+               );
 }
 
 
 static struct option opts[] = {
-       { .name = "genre",     .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "genre",      .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "smart",      .has_arg = 0, .flag = 0, .val = '2' },
+       { .name = "log",        .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = "netlink",    .has_arg = 0, .flag = 0, .val = '4' },
+       { .name = "connector",  .has_arg = 0, .flag = 0, .val = '5' },
        { .name = 0 }
 };
 
-
-static void init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
-
 static void parse_string(const unsigned char *s, struct ipt_osf_info *info)
 {
        if (strlen(s) < MAXGENRELEN) 
@@ -71,15 +74,40 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
        
        switch(c) 
        {
-               case '1':
-                       if (*flags)
-                               exit_error(PARAMETER_PROBLEM, "Can't specify multiple strings");
+               case '1': /* --genre */
+                       if (*flags & IPT_OSF_GENRE)
+                               exit_error(PARAMETER_PROBLEM, "Can't specify multiple genre parameter");
                        check_inverse(optarg, &invert, &optind, 0);
                        parse_string(argv[optind-1], info);
                        if (invert)
                                info->invert = 1;
                        info->len=strlen((char *)info->genre);
-                       *flags = 1;
+                       *flags |= IPT_OSF_GENRE;
+                       break;
+               case '2': /* --smart */
+                       if (*flags & IPT_OSF_SMART)
+                               exit_error(PARAMETER_PROBLEM, "Can't specify multiple smart parameter");
+                       *flags |= IPT_OSF_SMART;
+                       info->flags |= IPT_OSF_SMART;
+                       break;
+               case '3': /* --log */
+                       if (*flags & IPT_OSF_LOG)
+                               exit_error(PARAMETER_PROBLEM, "Can't specify multiple log parameter");
+                       *flags |= IPT_OSF_LOG;
+                       info->loglevel = atoi(argv[optind-1]);
+                       info->flags |= IPT_OSF_LOG;
+                       break;
+               case '4': /* --netlink */
+                       if (*flags & IPT_OSF_NETLINK)
+                               exit_error(PARAMETER_PROBLEM, "Can't specify multiple netlink parameter");
+                       *flags |= IPT_OSF_NETLINK;
+                       info->flags |= IPT_OSF_NETLINK;
+                       break;
+               case '5': /* --connector */
+                       if (*flags & IPT_OSF_CONNECTOR)
+                               exit_error(PARAMETER_PROBLEM, "Can't specify multiple connector parameter");
+                       *flags |= IPT_OSF_CONNECTOR;
+                       info->flags |= IPT_OSF_CONNECTOR;
                        break;
                default:
                        return 0;
@@ -115,7 +143,6 @@ static struct iptables_match osf_match = {
     .size          = IPT_ALIGN(sizeof(struct ipt_osf_info)),
     .userspacesize = IPT_ALIGN(sizeof(struct ipt_osf_info)),
     .help          = &help,
-    .init          = &init,
     .parse         = &parse,
     .final_check   = &final_check,
     .print         = &print,
diff --git a/extensions/libipt_osf.man b/extensions/libipt_osf.man
new file mode 100644 (file)
index 0000000..38d25a0
--- /dev/null
@@ -0,0 +1,47 @@
+The idea of passive OS fingerprint matching exists for quite a long time,
+but was created as extension fo OpenBSD pf only some weeks ago.
+Original idea was lurked in some OpenBSD mailing list (thanks
+grange@open...) and than adopted for Linux netfilter in form of this code.
+
+Original fingerprint table was created by Michal Zalewski <lcamtuf@coredump.cx>.
+
+This module compares some data(WS, MSS, options and it's order, ttl,
+df and others) from first SYN packet (actually from packets with SYN
+bit set) with dynamically loaded OS fingerprints.
+.TP
+.B "--log 1/0" 
+If present, OSF will log determined genres even if they don't match
+desired one.   
+0 - log all determined entries, 
+1 - only first one.
+
+In syslog you find something like this:
+.IP
+ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139
+.IP
+ipt_osf: Unknown: 16384:106:1:48:020405B401010402 44.33.22.11:1239 -> 11.22.33.44:80
+.TP
+.B "--smart"
+if present, OSF will use some smartness to determine remote OS.
+OSF will use initial TTL only if source of connection is in our local network.
+.TP
+.B "--netlink"
+If present, OSF will log all events also through netlink NETLINK_NFLOG groupt 1.
+.TP
+.BI "--genre " "[!] string"
+Match a OS genre by passive fingerprinting
+.P
+Example:
+
+#iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 1 --smart
+
+NOTE: -p tcp is obviously required as it is a TCP match.
+
+Fingerprints can be loaded and read through /proc/sys/net/ipv4/osf file.
+One can flush all fingerprints with following command:
+.IP
+echo -en FLUSH > /proc/sys/net/ipv4/osf
+.P
+Only one fingerprint per open/write/close.
+
+Fingerprints can be downloaded from http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os
index 73f0ed5..cf13cb9 100644 (file)
@@ -22,6 +22,7 @@ help(void)
 "[!] --pid-owner processid  Match local pid\n"
 "[!] --sid-owner sessionid  Match local sid\n"
 "[!] --cmd-owner name       Match local command name\n"
+"NOTE: pid, sid and command matching are broken on SMP\n"
 "\n",
 IPTABLES_VERSION);
 #else
@@ -31,6 +32,7 @@ IPTABLES_VERSION);
 "[!] --gid-owner groupid    Match local gid\n"
 "[!] --pid-owner processid  Match local pid\n"
 "[!] --sid-owner sessionid  Match local sid\n"
+"NOTE: pid and sid matching are broken on SMP\n"
 "\n",
 IPTABLES_VERSION);
 #endif /* IPT_OWNER_COMM */
@@ -47,14 +49,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this. */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -125,9 +119,10 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        case '5':
                check_inverse(optarg, &invert, &optind, 0);
                if(strlen(optarg) > sizeof(ownerinfo->comm))
-                       exit_error(PARAMETER_PROBLEM, "OWNER CMD `%s' too long, max %d characters", optarg, sizeof(ownerinfo->comm));
+                       exit_error(PARAMETER_PROBLEM, "OWNER CMD `%s' too long, max %u characters", optarg, (unsigned int)sizeof(ownerinfo->comm));
 
                strncpy(ownerinfo->comm, optarg, sizeof(ownerinfo->comm));
+               ownerinfo->comm[sizeof(ownerinfo->comm)-1] = '\0';
 
                if (invert)
                        ownerinfo->invert |= IPT_OWNER_COMM;
@@ -235,20 +230,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 #endif
 }
 
-static
-struct iptables_match owner
-= { NULL,
-    "owner",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_owner_info)),
-    IPT_ALIGN(sizeof(struct ipt_owner_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match owner = { 
+       .next           = NULL,
+       .name           = "owner",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_owner_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_owner_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_owner.man b/extensions/libipt_owner.man
new file mode 100644 (file)
index 0000000..b635e7d
--- /dev/null
@@ -0,0 +1,28 @@
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets.  It is only valid in the
+.B OUTPUT
+chain, and even this some packets (such as ICMP ping responses) may
+have no owner, and hence never match.
+.TP
+.BI "--uid-owner " "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner " "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner " "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner " "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.TP
+.BI "--cmd-owner " "name"
+Matches if the packet was created by a process with the given command name.
+(this option is present only if iptables was compiled under a kernel
+supporting this feature)
+.TP
+.B NOTE: pid, sid and command matching are broken on SMP
index 9149d90..b6dae2a 100644 (file)
@@ -34,45 +34,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* copied from iptables.c */
-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)
-               exit_error(PARAMETER_PROBLEM,
-                          "interface name `%s' must be shorter than IFNAMSIZ"
-                          " (%i)", arg, IFNAMSIZ-1);
-
-       strcpy(vianame, arg);
-       if (vialen == 0)
-               memset(mask, 0, IFNAMSIZ);
-       else if (vianame[vialen - 1] == '+') {
-               memset(mask, 0xFF, vialen - 1);
-               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
-               /* Don't remove `+' here! -HW */
-       } else {
-               /* Include nul-terminator in match */
-               memset(mask, 0xFF, vialen + 1);
-               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
-               for (i = 0; vianame[i]; i++) {
-                       if (!isalnum(vianame[i])
-                           && vianame[i] != '_'
-                           && vianame[i] != '.') {
-                               printf("Warning: wierd character in interface"
-                                      " `%s' (No aliases, :, ! or *).\n",
-                                      vianame);
-                               break;
-                       }
-               }
-       }
-}
-
 static void
 init(struct ipt_entry_match *m, unsigned int *nfcache)
 {
@@ -210,20 +171,19 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        printf(" ");
 }
 
-static
-struct iptables_match physdev
-= { NULL,
-    "physdev",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_physdev_info)),
-    IPT_ALIGN(sizeof(struct ipt_physdev_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match physdev = { 
+       .next           = NULL,
+       .name           = "physdev",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_physdev_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_physdev_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_physdev.man b/extensions/libipt_physdev.man
new file mode 100644 (file)
index 0000000..846ec7c
--- /dev/null
@@ -0,0 +1,42 @@
+This module matches on the bridge port input and output devices enslaved
+to a bridge device. This module is a part of the infrastructure that enables
+a transparent bridging IP firewall and is only useful for kernel versions
+above version 2.5.44.
+.TP
+.B --physdev-in name
+Name of a bridge port via which a packet is received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). If the interface name ends in a "+", then any
+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
+.B --physdev-out name
+Name of a bridge port via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+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 it is yet unknown what
+the output device will be, then the packet won't match this option, unless
+'!' is used.
+.TP
+.B --physdev-is-in
+Matches if the packet has entered through a bridge interface.
+.TP
+.B --physdev-is-out
+Matches if the packet will leave through a bridge interface.
+.TP
+.B --physdev-is-bridged
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
index 9141e48..ea6439e 100644 (file)
@@ -69,11 +69,6 @@ static struct option opts[] = {
        {0}
 };
 
-static void init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static void parse_pkttype(const char *pkttype, struct ipt_pkttype_info *info)
 {
        unsigned int    i;
@@ -152,20 +147,18 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_pkttype(info);
 }
 
-static
-struct iptables_match pkttype = {
-    NULL,
-    "pkttype",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_pkttype_info)),
-    IPT_ALIGN(sizeof(struct ipt_pkttype_info)),
-    &help,
-    &init,
-    &parse, 
-    &final_check, 
-    &print,
-    &save, 
-    opts
+static struct iptables_match pkttype = {
+       .next           = NULL,
+       .name           = "pkttype",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_pkttype_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_pkttype_info)),
+       .help           = &help,
+       .parse          = &parse, 
+       .final_check    = &final_check, 
+       .print          = &print,
+       .save           = &save, 
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_pkttype.man b/extensions/libipt_pkttype.man
new file mode 100644 (file)
index 0000000..b52810b
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the link-layer packet type.
+.TP
+.BI "--pkt-type " "[\fIunicast\fP|\fIbroadcast\fP|\fImulticast\fP]"
index 8a6198e..3d0034a 100644 (file)
@@ -56,8 +56,6 @@ init(struct ipt_entry_match *m, unsigned int *nfcache)
        psdinfo->delay_threshold = SCAN_DELAY_THRESHOLD;
        psdinfo->lo_ports_weight = PORT_WEIGHT_PRIV;
        psdinfo->hi_ports_weight = PORT_WEIGHT_HIGH;
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
 }
 
 
@@ -84,11 +82,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        struct ipt_psd_info *psdinfo = (struct ipt_psd_info *)(*match)->data;
        unsigned int num;
        
-       if (!optarg)
-               exit_error(PARAMETER_PROBLEM, "missing optarg");
-
-       /* string_to_number needs a leading space */
-
        switch (c) {
        /* PSD-weight-threshold */
        case '1':
@@ -180,20 +173,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        printf("--psd-hi-ports-weight %u ", psdinfo->hi_ports_weight);
 }
 
-static
-struct iptables_match psd
-= { NULL,
-    "psd",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_psd_info)),
-    IPT_ALIGN(sizeof(struct ipt_psd_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match psd = { 
+       .next           = NULL,
+       .name           = "psd",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_psd_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_psd_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_psd.man b/extensions/libipt_psd.man
new file mode 100644 (file)
index 0000000..b73fffc
--- /dev/null
@@ -0,0 +1,18 @@
+Attempt to detect TCP and UDP port scans. This match was derived from
+Solar Designer's scanlogd.
+.TP
+.BI "--psd-weight-threshold " "threshold"
+Total weight of the latest TCP/UDP packets with different
+destination ports coming from the same host to be treated as port
+scan sequence.
+.TP
+.BI "--psd-delay-threshold " "delay"
+Delay (in hundredths of second) for the packets with different
+destination ports coming from the same host to be treated as
+possible port scan subsequence.
+.TP
+.BI "--psd-lo-ports-weight " "weight"
+Weight of the packet with privileged (<=1024) destination port.
+.TP
+.BI "--psd-hi-ports-weight " "weight"
+Weight of the packet with non-priviliged destination port.
index 078f9e3..a9c138c 100644 (file)
@@ -24,14 +24,6 @@ help(void)
                " --quota quota                 quota (bytes)\n" "\n");
 }
 
-/* initialise match */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-        /* no can cache */
-        *nfcache |= NFC_UNKNOWN;
-}
-
 /* print matchinfo */
 static void
 print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric)
@@ -93,18 +85,18 @@ final_check(unsigned int flags)
 {
 }
 
-struct iptables_match quota = { NULL,
-        "quota",
-        IPTABLES_VERSION,
-        IPT_ALIGN(sizeof (struct ipt_quota_info)),
-        IPT_ALIGN(sizeof (struct ipt_quota_info)),
-        &help,
-        &init,
-        &parse,
-        &final_check,
-        &print,
-        &save,
-        opts
+struct iptables_match quota = { 
+       .next           = NULL,
+       .name           = "quota",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof (struct ipt_quota_info)),
+       .userspacesize  = IPT_ALIGN(sizeof (struct ipt_quota_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void
diff --git a/extensions/libipt_quota.man b/extensions/libipt_quota.man
new file mode 100644 (file)
index 0000000..8a07ec0
--- /dev/null
@@ -0,0 +1,7 @@
+Implements network quotas by decrementing a byte counter with each
+packet.
+.TP
+.BI "--quota " "bytes"
+The quota in bytes.
+.P
+KNOWN BUGS: this does not work on SMP systems.
index 97f09a4..d28ab8c 100644 (file)
@@ -35,7 +35,7 @@ help(void)
 {
        printf(
 "random v%s options:\n"
-"  [--average]     percent      The probability in percentage of the match\n"
+"  [--average      percent ]    The probability in percentage of the match\n"
 "                               If ommited, a probability of 50%% percent is set.\n"
 "                               Percentage must be within : 1 <= percent <= 99.\n\n",
 IPTABLES_VERSION);
@@ -51,7 +51,6 @@ static void
 init(struct ipt_entry_match *m, unsigned int *nfcache)
 {
        struct ipt_rand_info *randinfo = (struct ipt_rand_info *)(m)->data;
-       *nfcache |= NFC_UNKNOWN;
 
        /* We assign the average to be 50 which is our default value */
        /* 50 * 2.55 = 128 */
@@ -130,19 +129,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        printf("--average %u ", result.quot);
 }
 
-struct iptables_match rand_match
-= { NULL,
-    "random",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_rand_info)),
-    IPT_ALIGN(sizeof(struct ipt_rand_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct iptables_match rand_match = { 
+       .next           = NULL,
+       .name           = "random",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_rand_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_rand_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_random.man b/extensions/libipt_random.man
new file mode 100644 (file)
index 0000000..f808a77
--- /dev/null
@@ -0,0 +1,4 @@
+This module randomly matches a certain percentage of all packets.
+.TP
+.BI "--average " "percent"
+Matches the given percentage.  If omitted, a probability of 50% is set. 
index 60a3897..90e6089 100644 (file)
@@ -28,14 +28,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -49,7 +41,8 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        switch (c) {
                char *end;
        case '1':
-               check_inverse(optarg, &invert, &optind, 0);
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               optarg = argv[optind-1];
                realminfo->id = strtoul(optarg, &end, 0);
                if (*end == '/') {
                        realminfo->mask = strtoul(end+1, &end, 0);
@@ -69,12 +62,9 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 }
 
 static void
-print_realm(unsigned long id, unsigned long mask, int invert, int numeric)
+print_realm(unsigned long id, unsigned long mask)
 {
-       if (invert)
-               fputc('!', stdout);
-
-       if(mask != 0xffffffff)
+       if (mask != 0xffffffff)
                printf("0x%lx/0x%lx ", id, mask);
        else
                printf("0x%lx ", id);
@@ -86,10 +76,13 @@ print(const struct ipt_ip *ip,
       const struct ipt_entry_match *match,
       int numeric)
 {
+       struct ipt_realm_info *ri = (struct ipt_realm_info *) match->data;
+
+       if (ri->invert)
+               printf("! ");
+
        printf("REALM match ");
-       print_realm(((struct ipt_realm_info *)match->data)->id,
-                  ((struct ipt_realm_info *)match->data)->mask,
-                  ((struct ipt_realm_info *)match->data)->invert, numeric);
+       print_realm(ri->id, ri->mask);
 }
 
 
@@ -97,10 +90,13 @@ print(const struct ipt_ip *ip,
 static void
 save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 {
+       struct ipt_realm_info *ri = (struct ipt_realm_info *) match->data;
+
+       if (ri->invert)
+               printf("! ");
+
        printf("--realm ");
-       print_realm(((struct ipt_realm_info *)match->data)->id,
-                  ((struct ipt_realm_info *)match->data)->mask,
-                  ((struct ipt_realm_info *)match->data)->invert, 0);
+       print_realm(ri->id, ri->mask);
 }
 
 /* Final check; must have specified --mark. */
@@ -112,19 +108,17 @@ final_check(unsigned int flags)
                           "REALM match: You must specify `--realm'");
 }
 
-struct iptables_match realm
-= { NULL,
-    "realm",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_realm_info)),
-    IPT_ALIGN(sizeof(struct ipt_realm_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match realm = { NULL,
+       .name           = "realm",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_realm_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_realm_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_realm.man b/extensions/libipt_realm.man
new file mode 100644 (file)
index 0000000..55e67fc
--- /dev/null
@@ -0,0 +1,5 @@
+This matches the routing realm.  Routing realms are used in complex routing
+setups involving dynamic routing protocols like BGP.
+.TP
+.BI "--realm " "[!]" "value[/mask]"
+Matches a given realm number (and optionally mask).
index aa32aa0..0b0ed2d 100644 (file)
@@ -72,9 +72,11 @@ init(struct ipt_entry_match *match, unsigned int *nfcache)
 {
        struct ipt_recent_info *info = (struct ipt_recent_info *)(match)->data;
 
-       *nfcache |= NFC_UNKNOWN;
 
        strncpy(info->name,"DEFAULT",IPT_RECENT_NAME_LEN);
+       /* eventhough IPT_RECENT_NAME_LEN is currently defined as 200,
+        * better be safe, than sorry */
+       info->name[IPT_RECENT_NAME_LEN-1] = '\0';
        info->side = IPT_RECENT_SOURCE;
 }
 
@@ -142,6 +144,7 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
                case 208:
                        strncpy(info->name,optarg,IPT_RECENT_NAME_LEN);
+                       info->name[IPT_RECENT_NAME_LEN-1] = '\0';
                        break;
 
                case 209:
diff --git a/extensions/libipt_recent.man b/extensions/libipt_recent.man
new file mode 100644 (file)
index 0000000..bf5d710
--- /dev/null
@@ -0,0 +1,93 @@
+Allows you to dynamically create a list of IP addresses and then match
+against that list in a few different ways.
+
+For example, you can create a `badguy' list out of people attempting
+to connect to port 139 on your firewall and then DROP all future
+packets from them without considering them.
+.TP
+.BI "--name " "name"
+Specify the list to use for the commands. If no name is given then 'DEFAULT'
+will be used.
+.TP
+[\fB!\fR] \fB--set\fR
+This will add the source address of the packet to the list. If the
+source address is already in the list, this will update the existing
+entry. This will always return success (or failure if `!' is passed
+in).
+.TP
+[\fB!\fR] \fB--rcheck\fR
+Check if the source address of the packet is currently in
+the list.
+.TP
+[\fB!\fR] \fB--update\fR
+Like \fB--rcheck\fR, except it will update the "last seen" timestamp if it
+matches.
+.TP
+[\fB!\fR] \fB--remove\fR
+Check if the source address of the packet is currently in the list and
+if so that address will be removed from the list and the rule will
+return true. If the address is not found, false is returned.
+.TP
+[\fB!\fR] \fB--seconds \fIseconds\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and was seen within the last given
+number of seconds.
+.TP
+[\fB!\fR] \fB--hitcount \fIhits\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and packets had been received greater
+than or equal to the given value. This option may be used along with
+\fB--seconds\fR to create an even narrower match requiring a certain
+number of hits within a specific time frame.
+.TP
+\fB--rttl\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and the TTL of the current packet
+matches that of the packet which hit the \fB--set\fR rule. This may be
+useful if you have problems with people faking their source address in
+order to DoS you via this module by disallowing others access to your
+site by sending bogus packets to you.
+.P
+Examples:
+.IP
+# iptables -A FORWARD -m recent --name badguy --rcheck --seconds 60 -j DROP
+
+# iptables -A FORWARD -p tcp -i eth0 --dport 139 -m recent --name badguy --set -j DROP
+.P
+Official website (http://snowman.net/projects/ipt_recent/) also has
+some examples of usage.
+
+/proc/net/ipt_recent/* are the current lists of addresses and information 
+about each entry of each list.
+
+Each file in /proc/net/ipt_recent/ can be read from to see the current list
+or written two using the following commands to modify the list:
+.TP
+echo xx.xx.xx.xx > /proc/net/ipt_recent/DEFAULT
+to Add to the DEFAULT list
+.TP
+echo -xx.xx.xx.xx > /proc/net/ipt_recent/DEFAULT
+to Remove from the DEFAULT list
+.TP
+echo clear > /proc/net/ipt_recent/DEFAULT
+to empty the DEFAULT list.
+.P
+The module itself accepts parameters, defaults shown:
+.TP
+.BI "ip_list_tot=" "100"
+Number of addresses remembered per table
+.TP
+.BI "ip_pkt_list_tot=" "20"
+Number of packets per address remembered
+.TP
+.BI "ip_list_hash_size=" "0"
+Hash table size. 0 means to calculate it based on ip_list_tot, default: 512
+.TP
+.BI "ip_list_perms=" "0644"
+Permissions for /proc/net/ipt_recent/* files
+.TP
+.BI "debug=" "0"
+Set to 1 to get lots of debugging info
index 819c8ef..571d286 100644 (file)
@@ -16,14 +16,6 @@ static struct option opts[] = {
        {0}
 };
 
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this. */
-       *nfcache |= NFC_UNKNOWN;
-}
-
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -53,19 +45,18 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 }
 
 static
-struct iptables_match record_rpc
-= { NULL,
-    "record_rpc",
-    IPTABLES_VERSION,
-    IPT_ALIGN(0),
-    IPT_ALIGN(0),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct iptables_match record_rpc = { 
+       .next           = NULL,
+       .name           = "record_rpc",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
index 7fb0586..f6e897a 100644 (file)
@@ -180,8 +180,6 @@ static void init(struct ipt_entry_match *match, unsigned int *nfcache)
        struct ipt_rpc_info *rpcinfo = ((struct ipt_rpc_info *)match->data);
 
 
-       /* caching not yet implemented */
-        *nfcache |= NFC_UNKNOWN;
 
        /* initialise those funky user vars */
        rpcinfo->i_procs = -1;
@@ -352,18 +350,19 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 }
 
 
-static struct iptables_match rpcstruct = { NULL,
-    "rpc",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_rpc_info)),
-    IPT_ALIGN(sizeof(struct ipt_rpc_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match rpcstruct = { 
+       .next           = NULL,
+       .name           = "rpc",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_rpc_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_rpc_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 
index d512fa6..af35f9c 100644 (file)
 #include <stdlib.h>
 #include <getopt.h>
 #include <netdb.h>
+#include <ctype.h>
 
 #include <iptables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_sctp.h>
 
+#if 0
+#define DEBUGP(format, first...) printf(format, ##first)
+#define static
+#else
+#define DEBUGP(format, fist...) 
+#endif
+
+static void
+print_chunk(u_int32_t chunknum, int numeric);
+
 /* Initialize the match. */
 static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
+init(struct ipt_entry_match *m, 
+     unsigned int *nfcache)
 {
+       int i;
        struct ipt_sctp_info *einfo = (struct ipt_sctp_info *)m->data;
 
-       einfo->spts[1] = einfo->dpts[1] = 0xFFFF;
+       memset(einfo, 0, sizeof(struct ipt_sctp_info));
+
+       for (i = 0; i < IPT_NUM_SCTP_FLAGS; i++) {
+               einfo->flag_info[i].chunktype = -1;
+       }
 }
 
-static void help(void) 
+static void help(void)
 {
        printf(
 "SCTP match v%s options\n"
-" --sctp-chunks [!] mask comp  match when SCTP chunks & mask == comp\n"
-" --source-port [!] port[:port]\n"
+" --source-port [!] port[:port]                          match source port(s)\n"
 " --sport ...\n"
-"                               match source port(s)"
-" --destination-port [!] port[:port]\n"
-" --dport ...\n\n",
+" --destination-port [!] port[:port]                     match destination port(s)\n"
+" --dport ...\n" 
+" --chunk-types [!] (all|any|none) (chunktype[:flags])+        match if all, any or none of\n"
+"                                                      chunktypes are present\n"
+"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK ALL NONE\n",
        IPTABLES_VERSION);
 }
 
@@ -44,7 +62,7 @@ static struct option opts[] = {
        { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
        { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
        { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
-       { .name = "sctp-chunks", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = "chunk-types", .has_arg = 1, .flag = 0, .val = '3' },
        { .name = 0 }
 };
 
@@ -64,24 +82,27 @@ parse_sctp_port(const char *port)
 {
        unsigned int portnum;
 
+       DEBUGP("%s\n", port);
        if (string_to_number(port, 0, 65535, &portnum) != -1 ||
            (portnum = service_to_port(port)) != -1)
                return (u_int16_t)portnum;
 
        exit_error(PARAMETER_PROBLEM,
-                  "invalid TCP port/service `%s' specified", port);
+                  "invalid SCTP port/service `%s' specified", port);
 }
 
-
 static void
-parse_sctp_ports(const char *portstring, u_int16_t *ports)
+parse_sctp_ports(const char *portstring, 
+                u_int16_t *ports)
 {
        char *buffer;
        char *cp;
 
        buffer = strdup(portstring);
-       if ((cp = strchr(buffer, ':')) == NULL)
+       DEBUGP("%s\n", portstring);
+       if ((cp = strchr(buffer, ':')) == NULL) {
                ports[0] = ports[1] = parse_sctp_port(buffer);
+       }
        else {
                *cp = '\0';
                cp++;
@@ -98,51 +119,106 @@ parse_sctp_ports(const char *portstring, u_int16_t *ports)
 
 struct sctp_chunk_names {
        const char *name;
-       unsigned int flag;
+       unsigned int chunk_type;
+       const char *valid_flags;
 };
 
-/* FIXME: */
-#define ALL_CHUNKS     0xabcdef
+/*'ALL' and 'NONE' will be treated specially. */
 static struct sctp_chunk_names sctp_chunk_names[]
-= { { .name = "DATA",          .flag = (1 << 0) },
-    { .name = "INIT",          .flag = (1 << 1) },
-    { .name = "INIT_ACK",      .flag = (1 << 2) },
-    { .name = "SACK",          .flag = (1 << 3) },
-    { .name = "HEARTBEAT",     .flag = (1 << 4) },
-    { .name = "HEARTBEAT_ACK", .flag = (1 << 5) },
-    { .name = "ABORT",         .flag = (1 << 6) },
-    { .name = "SHUTDOWN",      .flag = (1 << 7) },
-    { .name = "SHUTDOWN_ACK",  .flag = (1 << 8) },
-    { .name = "ERROR",         .flag = (1 << 9) },
-    { .name = "COOKIE_ECHO",   .flag = (1 << 10) },
-    { .name = "COOKIE_ACK",    .flag = (1 << 11) },
-    { .name = "ECN_ECNE",      .flag = (1 << 12) },
-    { .name = "ECN_CWR",       .flag = (1 << 13) },
-    { .name = "SHUTDOWN_COMPLETE", .flag = (1 << 14) },
-    { .name = "ASCONF",                .flag = (1 << 31) },
-    { .name = "ASCONF_ACK",    .flag = (1 << 30) },
-    { .name = "ALL",           .flag = ALL_CHUNKS },
-    { .name = "NONE",          .flag = 0 },
+= { { .name = "DATA",          .chunk_type = 0,   .valid_flags = "-----UBE"},
+    { .name = "INIT",          .chunk_type = 1,   .valid_flags = "--------"},
+    { .name = "INIT_ACK",      .chunk_type = 2,   .valid_flags = "--------"},
+    { .name = "SACK",          .chunk_type = 3,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT",     .chunk_type = 4,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT_ACK", .chunk_type = 5,   .valid_flags = "--------"},
+    { .name = "ABORT",         .chunk_type = 6,   .valid_flags = "-------T"},
+    { .name = "SHUTDOWN",      .chunk_type = 7,   .valid_flags = "--------"},
+    { .name = "SHUTDOWN_ACK",  .chunk_type = 8,   .valid_flags = "--------"},
+    { .name = "ERROR",         .chunk_type = 9,   .valid_flags = "--------"},
+    { .name = "COOKIE_ECHO",   .chunk_type = 10,  .valid_flags = "--------"},
+    { .name = "COOKIE_ACK",    .chunk_type = 11,  .valid_flags = "--------"},
+    { .name = "ECN_ECNE",      .chunk_type = 12,  .valid_flags = "--------"},
+    { .name = "ECN_CWR",       .chunk_type = 13,  .valid_flags = "--------"},
+    { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14,  .valid_flags = "-------T"},
+    { .name = "ASCONF",                .chunk_type = 31,  .valid_flags = "--------"},
+    { .name = "ASCONF_ACK",    .chunk_type = 30,  .valid_flags = "--------"},
 };
 
+static void
+save_chunk_flag_info(struct ipt_sctp_flag_info *flag_info,
+                    int *flag_count,
+                    int chunktype, 
+                    int bit, 
+                    int set)
+{
+       int i;
+
+       for (i = 0; i < *flag_count; i++) {
+               if (flag_info[i].chunktype == chunktype) {
+                       DEBUGP("Previous match found\n");
+                       flag_info[i].chunktype = chunktype;
+                       flag_info[i].flag_mask |= (1 << bit);
+                       if (set) {
+                               flag_info[i].flag |= (1 << bit);
+                       }
+
+                       return;
+               }
+       }
+       
+       if (*flag_count == IPT_NUM_SCTP_FLAGS) {
+               exit_error (PARAMETER_PROBLEM,
+                       "Number of chunk types with flags exceeds currently allowed limit."
+                       "Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and"
+                       "recompiling both the kernel space and user space modules\n");
+       }
+
+       flag_info[*flag_count].chunktype = chunktype;
+       flag_info[*flag_count].flag_mask |= (1 << bit);
+       if (set) {
+               flag_info[*flag_count].flag |= (1 << bit);
+       }
+       (*flag_count)++;
+}
 
-static unsigned int
-parse_sctp_chunk(const char *flags)
+static void
+parse_sctp_chunk(struct ipt_sctp_info *einfo, 
+                const char *chunks)
 {
-       unsigned int ret = 0;
        char *ptr;
        char *buffer;
+       unsigned int i, j;
+       int found = 0;
+       char *chunk_flags;
+
+       buffer = strdup(chunks);
+       DEBUGP("Buffer: %s\n", buffer);
 
-       buffer = strdup(flags);
+       SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+
+       if (!strcasecmp(buffer, "ALL")) {
+               SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap);
+               goto out;
+       }
+       
+       if (!strcasecmp(buffer, "NONE")) {
+               SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+               goto out;
+       }
 
        for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
-               unsigned int i;
-               int found = 0;
-               for (i = 0;
-                    i < sizeof(sctp_chunk_names)/sizeof(struct sctp_chunk_names);
-                    i++) {
+               found = 0;
+               DEBUGP("Next Chunk type %s\n", ptr);
+               
+               if ((chunk_flags = strchr(ptr, ':')) != NULL) {
+                       *chunk_flags++ = 0;
+               }
+               
+               for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
                        if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
-                               ret |= sctp_chunk_names[i].flag;
+                               DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type);
+                               SCTP_CHUNKMAP_SET(einfo->chunkmap, 
+                                       sctp_chunk_names[i].chunk_type);
                                found = 1;
                                break;
                        }
@@ -150,29 +226,53 @@ parse_sctp_chunk(const char *flags)
                if (!found)
                        exit_error(PARAMETER_PROBLEM,
                                   "Unknown sctp chunk `%s'", ptr);
-       }
 
+               if (chunk_flags) {
+                       DEBUGP("Chunk flags %s\n", chunk_flags);
+                       for (j = 0; j < strlen(chunk_flags); j++) {
+                               char *p;
+                               int bit;
+
+                               if ((p = strchr(sctp_chunk_names[i].valid_flags, 
+                                               toupper(chunk_flags[j]))) != NULL) {
+                                       bit = p - sctp_chunk_names[i].valid_flags;
+                                       bit = 7 - bit;
+
+                                       save_chunk_flag_info(einfo->flag_info, 
+                                               &(einfo->flag_count), i, bit, 
+                                               isupper(chunk_flags[j]));
+                               } else {
+                                       exit_error(PARAMETER_PROBLEM, 
+                                               "Invalid flags for chunk type %d\n", i);
+                               }
+                       }
+               }
+       }
+out:
        free(buffer);
-       return ret;
 }
 
 static void
 parse_sctp_chunks(struct ipt_sctp_info *einfo,
-               const char *mask,
-               const char *cmp,
-               int invert)
+                 const char *match_type,
+                 const char *chunks)
 {
-       einfo->chunks = parse_sctp_chunk(mask);
-       einfo->chunk_mask = parse_sctp_chunk(cmp);
+       DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks);
+       if (!strcasecmp(match_type, "ANY")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY;
+       } else  if (!strcasecmp(match_type, "ALL")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL;
+       } else  if (!strcasecmp(match_type, "ONLY")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY;
+       } else {
+               exit_error (PARAMETER_PROBLEM, 
+                       "Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\"");
+       }
 
-       if (invert)
-               einfo->invflags |= IPT_SCTP_INV_CHUNKS;
+       SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+       parse_sctp_chunk(einfo, chunks);
 }
 
-#define SCTP_SRC_PORTS 0x01
-#define SCTP_DST_PORTS 0x02
-#define SCTP_CHUNKS    0x03
-
 static int
 parse(int c, char **argv, int invert, unsigned int *flags,
       const struct ipt_entry *entry,
@@ -184,48 +284,51 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch (c) {
        case '1':
-               if (*flags & SCTP_SRC_PORTS)
+               if (*flags & IPT_SCTP_SRC_PORTS)
                        exit_error(PARAMETER_PROBLEM,
                                   "Only one `--source-port' allowed");
+               einfo->flags |= IPT_SCTP_SRC_PORTS;
                check_inverse(optarg, &invert, &optind, 0);
                parse_sctp_ports(argv[optind-1], einfo->spts);
                if (invert)
-                       einfo->invflags |= IPT_SCTP_INV_SRCPT;
-               *flags |= SCTP_SRC_PORTS;
-               *nfcache |= NFC_IP_SRC_PT;
+                       einfo->invflags |= IPT_SCTP_SRC_PORTS;
+               *flags |= IPT_SCTP_SRC_PORTS;
                break;
 
        case '2':
-               if (*flags & SCTP_DST_PORTS)
+               if (*flags & IPT_SCTP_DEST_PORTS)
                        exit_error(PARAMETER_PROBLEM,
                                   "Only one `--destination-port' allowed");
+               einfo->flags |= IPT_SCTP_DEST_PORTS;
                check_inverse(optarg, &invert, &optind, 0);
                parse_sctp_ports(argv[optind-1], einfo->dpts);
                if (invert)
-                       einfo->invflags |= IPT_SCTP_INV_DSTPT;
-               *flags |= SCTP_DST_PORTS;
-               *nfcache |= NFC_IP_DST_PT;
+                       einfo->invflags |= IPT_SCTP_DEST_PORTS;
+               *flags |= IPT_SCTP_DEST_PORTS;
                break;
 
        case '3':
-               if (*flags & SCTP_CHUNKS)
+               if (*flags & IPT_SCTP_CHUNK_TYPES)
                        exit_error(PARAMETER_PROBLEM,
-                                  "Only one `--sctp-chunks' allowed");
+                                  "Only one `--chunk-types' allowed");
                check_inverse(optarg, &invert, &optind, 0);
 
                if (!argv[optind] 
                    || argv[optind][0] == '-' || argv[optind][0] == '!')
                        exit_error(PARAMETER_PROBLEM,
-                                  "--sctp-chunks requires two args");
+                                  "--chunk-types requires two args");
 
-               parse_sctp_chunks(einfo, argv[optind-1], argv[optind], invert);
+               einfo->flags |= IPT_SCTP_CHUNK_TYPES;
+               parse_sctp_chunks(einfo, argv[optind-1], argv[optind]);
+               if (invert)
+                       einfo->invflags |= IPT_SCTP_CHUNK_TYPES;
                optind++;
-               *flags |= SCTP_CHUNKS;
+               *flags |= IPT_SCTP_CHUNK_TYPES;
                break;
+
        default:
                return 0;
        }
-
        return 1;
 }
 
@@ -278,41 +381,89 @@ print_ports(const char *name, u_int16_t min, u_int16_t max,
 }
 
 static void
-print_chunk(u_int32_t chunks)
+print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask)
 {
-       unsigned int have_flag = 0;
+       int i;
 
-       while (chunks) {
-               unsigned int i;
+       DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, 
+                       chunk_flags_mask);
 
-               for (i = 0; (chunks & sctp_chunk_names[i].flag) == 0; i++);
+       if (chunk_flags_mask) {
+               printf(":");
+       }
 
-               if (have_flag)
-                       printf(",");
-               printf("%s", sctp_chunk_names[i].name);
-               have_flag = 1;
+       for (i = 7; i >= 0; i--) {
+               if (chunk_flags_mask & (1 << i)) {
+                       if (chunk_flags & (1 << i)) {
+                               printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
+                       } else {
+                               printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
+                       }
+               }
+       }
+}
 
-               chunks &= ~sctp_chunk_names[i].flag;
+static void
+print_chunk(u_int32_t chunknum, int numeric)
+{
+       if (numeric) {
+               printf("0x%04X", chunknum);
        }
+       else {
+               int i;
 
-       if (!have_flag)
-               printf("NONE");
+               for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
+                       if (sctp_chunk_names[i].chunk_type == chunknum)
+                               printf("%s", sctp_chunk_names[chunknum].name);
+               }
+       }
 }
 
 static void
-print_chunks(u_int32_t mask, u_int32_t cmp, int invert, int numeric)
+print_chunks(u_int32_t chunk_match_type, 
+            const u_int32_t *chunkmap, 
+            const struct ipt_sctp_flag_info *flag_info,
+            int flag_count,
+            int numeric)
 {
-       if (mask || invert) {
-               printf("flags:%s", invert ? "!" : "");
-               if (numeric)
-                       printf("0x%04X/0x%04X ", mask, cmp);
-               else {
-                       print_chunk(mask);
-                       printf("/");
-                       print_chunk(cmp);
-                       printf(" ");
+       int i, j;
+       int flag;
+
+       switch (chunk_match_type) {
+               case SCTP_CHUNK_MATCH_ANY:      printf("any "); break;
+               case SCTP_CHUNK_MATCH_ALL:      printf("all "); break;
+               case SCTP_CHUNK_MATCH_ONLY:     printf("only "); break;
+               default:        printf("Never reach herer\n"); break;
+       }
+
+       if (SCTP_CHUNKMAP_IS_CLEAR(chunkmap)) {
+               printf("NONE ");
+               goto out;
+       }
+       
+       if (SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)) {
+               printf("ALL ");
+               goto out;
+       }
+       
+       flag = 0;
+       for (i = 0; i < 256; i++) {
+               if (SCTP_CHUNKMAP_IS_SET(chunkmap, i)) {
+                       flag && printf(",");
+                       flag = 1;
+                       print_chunk(i, numeric);
+                       for (j = 0; j < flag_count; j++) {
+                               if (flag_info[j].chunktype == i) {
+                                       print_chunk_flags(i, flag_info[j].flag,
+                                               flag_info[j].flag_mask);
+                               }
+                       }
                }
        }
+
+       flag && printf(" ");
+out:
+       return;
 }
 
 /* Prints out the matchinfo. */
@@ -326,28 +477,39 @@ print(const struct ipt_ip *ip,
 
        printf("sctp ");
 
-       print_ports("spt", einfo->spts[0], einfo->spts[1],
-                   einfo->invflags & IPT_SCTP_INV_SRCPT,
-                   numeric);
-       print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
-                   einfo->invflags & IPT_SCTP_INV_DSTPT,
-                   numeric);
+       if (einfo->flags & IPT_SCTP_SRC_PORTS) {
+               print_ports("spt", einfo->spts[0], einfo->spts[1],
+                       einfo->invflags & IPT_SCTP_SRC_PORTS,
+                       numeric);
+       }
 
-       print_chunks(einfo->chunks, einfo->chunk_mask,
-                    einfo->invflags & ~IPT_SCTP_INV_MASK,
-                    numeric);
+       if (einfo->flags & IPT_SCTP_DEST_PORTS) {
+               print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+                       einfo->invflags & IPT_SCTP_DEST_PORTS,
+                       numeric);
+       }
+
+       if (einfo->flags & IPT_SCTP_CHUNK_TYPES) {
+               /* FIXME: print_chunks() is used in save() where the printing of '!'
+               s taken care of, so we need to do that here as well */
+               if (einfo->invflags & IPT_SCTP_CHUNK_TYPES) {
+                       printf("! ");
+               }
+               print_chunks(einfo->chunk_match_type, einfo->chunkmap,
+                       einfo->flag_info, einfo->flag_count, numeric);
+       }
 }
 
 /* Saves the union ipt_matchinfo in parsable form to stdout. */
 static void
-save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+save(const struct ipt_ip *ip, 
+     const struct ipt_entry_match *match)
 {
        const struct ipt_sctp_info *einfo =
                (const struct ipt_sctp_info *)match->data;
 
-       if (einfo->spts[0] != 0
-           || einfo->spts[1] != 0xFFFF) {
-               if (einfo->invflags & IPT_SCTP_INV_SRCPT)
+       if (einfo->flags & IPT_SCTP_SRC_PORTS) {
+               if (einfo->invflags & IPT_SCTP_SRC_PORTS)
                        printf("! ");
                if (einfo->spts[0] != einfo->spts[1])
                        printf("--sport %u:%u ", 
@@ -356,9 +518,8 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
                        printf("--sport %u ", einfo->spts[0]);
        }
 
-       if (einfo->dpts[0] != 0
-           || einfo->dpts[1] != 0xFFFF) {
-               if (einfo->invflags & IPT_SCTP_INV_DSTPT)
+       if (einfo->flags & IPT_SCTP_DEST_PORTS) {
+               if (einfo->invflags & IPT_SCTP_DEST_PORTS)
                        printf("! ");
                if (einfo->dpts[0] != einfo->dpts[1])
                        printf("--dport %u:%u ",
@@ -367,17 +528,13 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
                        printf("--dport %u ", einfo->dpts[0]);
        }
 
-       if (einfo->chunks
-           || (einfo->invflags & IPT_SCTP_INV_CHUNKS)) {
-               if (einfo->invflags & IPT_SCTP_INV_CHUNKS)
+       if (einfo->flags & IPT_SCTP_CHUNK_TYPES) {
+               if (einfo->invflags & IPT_SCTP_CHUNK_TYPES)
                        printf("! ");
-               printf("--sctp-chunks ");
-               if (einfo->chunks != ALL_CHUNKS) {
-                       print_chunk(einfo->chunks);
-               }
-               printf(" ");
-               print_chunk(einfo->chunk_mask);
-               printf(" ");
+               printf("--chunk-types ");
+
+               print_chunks(einfo->chunk_match_type, einfo->chunkmap, 
+                       einfo->flag_info, einfo->flag_count, 0);
        }
 }
 
@@ -400,3 +557,4 @@ void _init(void)
 {
        register_match(&sctp);
 }
+
diff --git a/extensions/libipt_sctp.man b/extensions/libipt_sctp.man
new file mode 100644 (file)
index 0000000..97b467d
--- /dev/null
@@ -0,0 +1,28 @@
+.TP
+\fB--source-port\fR,\fB--sport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--destination-port\fR,\fB--dport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--chunk-types\fR [\fB!\fR] \fBall\fR|\fBany\fR|\fBonly \fIchunktype\fR[\fB:\fIflags\fR] [...]
+The flag letter in upper case indicates that the flag is to match if set,
+in the lower case indicates to match if unset.
+
+Chunk types: DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK
+
+chunk type            available flags      
+.br
+DATA                  U B E u b e         
+.br
+ABORT                 T t                 
+.br
+SHUTDOWN_COMPLETE     T t                 
+
+(lowercase means flag should be "off", uppercase means "on")
+.P
+Examples:
+
+iptables -A INPUT -p sctp --dport 80 -j DROP
+
+iptables -A INPUT -p sctp --chunk-types any DATA,INIT -j DROP
+
+iptables -A INPUT -p sctp --chunk-types any DATA:Be -j ACCEPT
diff --git a/extensions/libipt_set.c b/extensions/libipt_set.c
new file mode 100644 (file)
index 0000000..e485f05
--- /dev/null
@@ -0,0 +1,167 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ *                         Patrick Schaaf <bof@bof.de>
+ *                         Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2004 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 version 2 as
+ * published by the Free Software Foundation.  
+ */
+
+/* Shared library add-on to iptables to add IP set matching. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_set.h>
+#include "libipt_set.h"
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf("set v%s options:\n"
+              " [!] --set     name flags\n"
+              "                'name' is the set name from to match,\n" 
+              "                'flags' are the comma separated list of\n"
+              "                'src' and 'dst'.\n"
+              "\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {"set", 1, 0, '1'},
+       {0}
+};
+
+/* Initialize the match. */
+static void init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_set_info_match *info = 
+               (struct ipt_set_info_match *) match->data;
+       
+
+       memset(info, 0, sizeof(struct ipt_set_info_match));
+
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache, struct ipt_entry_match **match)
+{
+       struct ipt_set_info_match *myinfo = 
+               (struct ipt_set_info_match *) (*match)->data;
+       struct ipt_set_info *info = &myinfo->match_set;
+
+       switch (c) {
+       case '1':               /* --set <set> <flag>[,<flag> */
+               if (info->flags[0])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--set can be specified only once");
+
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       info->flags[0] |= IPSET_MATCH_INV;
+
+               if (!argv[optind]
+                   || argv[optind][0] == '-'
+                   || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--set requires two args.");
+
+               if (strlen(argv[optind-1]) > IP_SET_MAXNAMELEN - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "setname `%s' too long, max %d characters.",
+                                  argv[optind-1], IP_SET_MAXNAMELEN - 1);
+
+               get_set_byname(argv[optind - 1], info);
+               parse_bindings(argv[optind], info);
+               DEBUGP("parse: set index %u\n", info->index);
+               optind++;
+               
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified --set. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "You must specify `--set' with proper arguments");
+       DEBUGP("final check OK\n");
+}
+
+static void
+print_match(const char *prefix, const struct ipt_set_info *info)
+{
+       int i;
+       char setname[IP_SET_MAXNAMELEN];
+
+       get_set_byid(setname, info->index);
+       printf("%s%s %s", 
+              (info->flags[0] & IPSET_MATCH_INV) ? "!" : "",
+              prefix,
+              setname); 
+       for (i = 0; i < IP_SET_MAX_BINDINGS; i++) {
+               if (!info->flags[i])
+                       break;          
+               printf("%s%s",
+                      i == 0 ? " " : ",",
+                      info->flags[i] & IPSET_SRC ? "src" : "dst");
+       }
+       printf(" ");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match, int numeric)
+{
+       struct ipt_set_info_match *info = 
+               (struct ipt_set_info_match *) match->data;
+
+       print_match("set", &info->match_set);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip,
+                const struct ipt_entry_match *match)
+{
+       struct ipt_set_info_match *info = 
+               (struct ipt_set_info_match *) match->data;
+
+       print_match("--set", &info->match_set);
+}
+
+static
+struct iptables_match set = {
+       .name           = "set",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_set_info_match)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_set_info_match)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&set);
+}
diff --git a/extensions/libipt_set.h b/extensions/libipt_set.h
new file mode 100644 (file)
index 0000000..0d6b329
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef _LIBIPT_SET_H
+#define _LIBIPT_SET_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static void
+parse_bindings(const char *optarg, struct ipt_set_info *info)
+{
+       char *saved = strdup(optarg);
+       char *ptr, *tmp = saved;
+       int i = 0;
+       
+       while (i < (IP_SET_MAX_BINDINGS - 1) && tmp != NULL) {
+               ptr = strsep(&tmp, ",");
+               if (strncmp(ptr, "src", 3) == 0)
+                       info->flags[i++] |= IPSET_SRC;
+               else if (strncmp(ptr, "dst", 3) == 0)
+                       info->flags[i++] |= IPSET_DST;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "You must spefify (the comma separated list of) 'src' or 'dst'.");
+       }
+
+       if (tmp)
+               exit_error(PARAMETER_PROBLEM,
+                          "Can't follow bindings deeper than %i.", 
+                          IP_SET_MAX_BINDINGS - 1);
+
+       free(saved);
+}
+
+static int get_set_getsockopt(void *data, size_t * size)
+{
+       int sockfd = -1;
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               exit_error(OTHER_PROBLEM,
+                          "Can't open socket to ipset.\n");
+       /* Send! */
+       return getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+}
+
+static void get_set_byname(const char *setname, struct ipt_set_info *info)
+{
+       struct ip_set_req_get_set req;
+       int size = sizeof(struct ip_set_req_get_set);
+       int res;
+
+       req.op = IP_SET_OP_GET_BYNAME;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       strncpy(req.set.name, setname, IP_SET_MAXNAMELEN);
+       req.set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+       res = get_set_getsockopt(&req, &size);
+       if (res != 0)
+               exit_error(OTHER_PROBLEM,
+                          "Problem when communicating with ipset, errno=%d.\n",
+                          errno);
+       if (size != sizeof(struct ip_set_req_get_set))
+               exit_error(OTHER_PROBLEM,
+                          "Incorrect return size from kernel during ipset lookup, "
+                          "(want %d, got %d)\n",
+                          sizeof(struct ip_set_req_get_set), size);
+       if (req.set.index == IP_SET_INVALID_ID)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set %s doesn't exist.\n", setname);
+
+       info->index = req.set.index;
+}
+
+static void get_set_byid(char * setname, ip_set_id_t index)
+{
+       struct ip_set_req_get_set req;
+       int size = sizeof(struct ip_set_req_get_set);
+       int res;
+
+       req.op = IP_SET_OP_GET_BYINDEX;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       req.set.index = index;
+       res = get_set_getsockopt(&req, &size);
+       if (res != 0)
+               exit_error(OTHER_PROBLEM,
+                          "Problem when communicating with ipset, errno=%d.\n",
+                          errno);
+       if (size != sizeof(struct ip_set_req_get_set))
+               exit_error(OTHER_PROBLEM,
+                          "Incorrect return size from kernel during ipset lookup, "
+                          "(want %d, got %d)\n",
+                          sizeof(struct ip_set_req_get_set), size);
+       if (req.set.name[0] == '\0')
+               exit_error(PARAMETER_PROBLEM,
+                          "Set id %i in kernel doesn't exist.\n", index);
+
+       strncpy(setname, req.set.name, IP_SET_MAXNAMELEN);
+}
+
+#endif /*_LIBIPT_SET_H*/
diff --git a/extensions/libipt_set.man b/extensions/libipt_set.man
new file mode 100644 (file)
index 0000000..d280577
--- /dev/null
@@ -0,0 +1,17 @@
+This modules macthes IP sets which can be defined by ipset(8).
+.TP
+.BR "--set " "setname flag[,flag...]"
+where flags are
+.BR "src"
+and/or
+.BR "dst" 
+and there can be no more than six of them. Hence the command
+.nf
+ iptables -A FORWARD -m set --set test src,dst
+.fi
+will match packets, for which (depending on the type of the set) the source
+address or port number of the packet can be found in the specified set. If 
+there is a binding belonging to the mached set element or there is a default 
+binding for the given set, then the rule will match the packet only if 
+additionally (depending on the type of the set) the destination address or 
+port number of the packet can be found in the set according to the binding.
index d79ad7c..4c5a3f5 100644 (file)
@@ -48,19 +48,19 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
 }
 
 static
-struct iptables_target standard
-= { NULL,
-    "standard",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(int)),
-    IPT_ALIGN(sizeof(int)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    NULL, /* print */
-    &save,
-    opts
+struct iptables_target standard = { 
+       .next           = NULL,
+       .name           = "standard",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(int)),
+       .userspacesize  = IPT_ALIGN(sizeof(int)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = NULL,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
index 3662d94..acafe9a 100644 (file)
@@ -28,14 +28,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static int
 parse_state(const char *state, size_t strlen, struct ipt_state_info *sinfo)
 {
@@ -151,20 +143,18 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_state(sinfo->statemask);
 }
 
-static
-struct iptables_match state
-= { NULL,
-    "state",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_state_info)),
-    IPT_ALIGN(sizeof(struct ipt_state_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match state = { 
+       .next           = NULL,
+       .name           = "state",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_state_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_state_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_state.man b/extensions/libipt_state.man
new file mode 100644 (file)
index 0000000..7107868
--- /dev/null
@@ -0,0 +1,21 @@
+This module, when combined with connection tracking, allows access to
+the connection tracking state for this packet.
+.TP
+.BI "--state " "state"
+Where state is a comma separated list of the connection states to
+match.  Possible states are
+.B INVALID
+meaning that the packet could not be identified for some reason which
+includes running out of memory and ICMP errors which don't correspond to any
+known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
index b4710f9..508eb90 100644 (file)
@@ -3,6 +3,13 @@
  * Copyright (C) 2000 Emmanuel Roger  <winfield@freegates.be>
  *
  * ChangeLog
+ *     29.12.2003: Michael Rash <mbr@cipherdyne.org>
+ *             Fixed iptables save/restore for ascii strings
+ *             that contain space chars, and hex strings that
+ *             contain embedded NULL chars.  Updated to print
+ *             strings in hex mode if any non-printable char
+ *             is contained within the string.
+ *
  *     27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
  *             Changed --tos to --string in save(). Also
  *             updated to work with slightly modified
@@ -37,15 +44,6 @@ static struct option opts[] = {
        { .name = 0 }
 };
 
-
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
-
 static void
 parse_string(const unsigned char *s, struct ipt_string_info *info)
 {      
@@ -77,8 +75,12 @@ parse_hex_string(const unsigned char *s, struct ipt_string_info *info)
                } else if (s[i] == '|') {
                        if (hex_f)
                                hex_f = 0;
-                       else
+                       else {
                                hex_f = 1;
+                               /* get past any initial whitespace just after the '|' */
+                               while (s[i+1] == ' ')
+                                       i++;
+                       }
                        if (i+1 >= slen)
                                break;
                        else
@@ -178,9 +180,53 @@ final_check(unsigned int flags)
 {
        if (!flags)
                exit_error(PARAMETER_PROBLEM,
-                          "STRING match: You must specify `--string'");
+                          "STRING match: You must specify `--string' or `--hex-string'");
 }
 
+/* Test to see if the string contains non-printable chars or quotes */
+static unsigned short int
+is_hex_string(const char *str, const unsigned short int len)
+{
+       unsigned int i;
+       for (i=0; i < len; i++)
+               if (! isprint(str[i]))
+                       return 1;  /* string contains at least one non-printable char */
+       /* use hex output if the last char is a "\" */
+       if ((unsigned char) str[len-1] == 0x5c)
+               return 1;
+       return 0;
+}
+
+/* Print string with "|" chars included as one would pass to --hex-string */
+static void
+print_hex_string(const char *str, const unsigned short int len)
+{
+       unsigned int i;
+       /* start hex block */
+       printf("\"|");
+       for (i=0; i < len; i++) {
+               /* see if we need to prepend a zero */
+               if ((unsigned char) str[i] <= 0x0F)
+                       printf("0%x", (unsigned char) str[i]);
+               else
+                       printf("%x", (unsigned char) str[i]);
+       }
+       /* close hex block */
+       printf("|\" ");
+}
+
+static void
+print_string(const char *str, const unsigned short int len)
+{
+       unsigned int i;
+       printf("\"");
+       for (i=0; i < len; i++) {
+               if ((unsigned char) str[i] == 0x22)  /* escape any embedded quotes */
+                       printf("%c", 0x5c);
+               printf("%c", (unsigned char) str[i]);
+       }
+       printf("\" ");  /* closing space and quote */
+}
 
 /* Prints out the matchinfo. */
 static void
@@ -191,7 +237,13 @@ print(const struct ipt_ip *ip,
        const struct ipt_string_info *info =
            (const struct ipt_string_info*) match->data;
 
-       printf("STRING match %s%s ", (info->invert) ? "!" : "", info->string);
+       if (is_hex_string(info->string, info->len)) {
+               printf("STRING match %s", (info->invert) ? "!" : "");
+               print_hex_string(info->string, info->len);
+       } else {
+               printf("STRING match %s", (info->invert) ? "!" : "");
+               print_string(info->string, info->len);
+       }
 }
 
 
@@ -202,7 +254,13 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        const struct ipt_string_info *info =
            (const struct ipt_string_info*) match->data;
 
-       printf("--string %s%s ", (info->invert) ? "! ": "", info->string);
+       if (is_hex_string(info->string, info->len)) {
+               printf("--hex-string %s", (info->invert) ? "! ": "");
+               print_hex_string(info->string, info->len);
+       } else {
+               printf("--string %s", (info->invert) ? "! ": "");
+               print_string(info->string, info->len);
+       }
 }
 
 
@@ -212,7 +270,6 @@ static struct iptables_match string = {
     .size          = IPT_ALIGN(sizeof(struct ipt_string_info)),
     .userspacesize = IPT_ALIGN(sizeof(struct ipt_string_info)),
     .help          = &help,
-    .init          = &init,
     .parse         = &parse,
     .final_check   = &final_check,
     .print         = &print,
index 37613ac..f8ed249 100644 (file)
@@ -187,7 +187,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        tcpinfo->invflags |= IPT_TCP_INV_SRCPT;
                *flags |= TCP_SRC_PORTS;
-               *nfcache |= NFC_IP_SRC_PT;
                break;
 
        case '2':
@@ -199,7 +198,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        tcpinfo->invflags |= IPT_TCP_INV_DSTPT;
                *flags |= TCP_DST_PORTS;
-               *nfcache |= NFC_IP_DST_PT;
                break;
 
        case '3':
@@ -207,9 +205,8 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                        exit_error(PARAMETER_PROBLEM,
                                   "Only one of `--syn' or `--tcp-flags' "
                                   " allowed");
-               parse_tcp_flags(tcpinfo, "SYN,RST,ACK", "SYN", invert);
+               parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert);
                *flags |= TCP_FLAGS;
-               *nfcache |= NFC_IP_TCPFLAGS;
                break;
 
        case '4':
@@ -228,7 +225,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                                invert);
                optind++;
                *flags |= TCP_FLAGS;
-               *nfcache |= NFC_IP_TCPFLAGS;
                break;
 
        case '5':
@@ -240,7 +236,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        tcpinfo->invflags |= IPT_TCP_INV_OPTION;
                *flags |= TCP_OPTION;
-               *nfcache |= NFC_IP_PROTO_UNKNOWN;
                break;
 
        default:
@@ -423,20 +418,20 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        }
 }
 
-static
-struct iptables_match tcp
-= { NULL,
-    "tcp",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_tcp)),
-    IPT_ALIGN(sizeof(struct ipt_tcp)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts };
+static struct iptables_match tcp = { 
+       .next           = NULL,
+       .name           = "tcp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tcp)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tcp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
 
 void
 _init(void)
diff --git a/extensions/libipt_tcp.man b/extensions/libipt_tcp.man
new file mode 100644 (file)
index 0000000..e1f4405
--- /dev/null
@@ -0,0 +1,49 @@
+These extensions are loaded if `--protocol tcp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is a convenient alias for this option.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.  The flag
+.B --dport
+is a convenient alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified.  The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set.  Flags are:
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK,RST and FIN bits
+cleared.  Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK,FIN SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
+.TP
+.BR "--mss " "\fIvalue\fP[:\fIvalue\fP]"
+Match TCP SYN or SYN/ACK packets with the specified MSS value (or range),
+which control the maximum packet size for that connection.
index 2eb9797..9a399bb 100644 (file)
@@ -24,13 +24,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_IP_PROTO_UNKNOWN;
-}
-
 static u_int16_t
 parse_tcp_mssvalue(const char *mssvalue)
 {
@@ -139,20 +132,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
                     mssinfo->invert, 0);
 }
 
-static
-struct iptables_match tcpmss
-= { NULL,
-    "tcpmss",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_tcpmss_match_info)),
-    IPT_ALIGN(sizeof(struct ipt_tcpmss_match_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match tcpmss = {
+       .next           = NULL,
+       .name           = "tcpmss",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tcpmss_match_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tcpmss_match_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_tcpmss.man b/extensions/libipt_tcpmss.man
new file mode 100644 (file)
index 0000000..5115d6b
--- /dev/null
@@ -0,0 +1,4 @@
+This matches the TCP MSS (maximum segment size) field of the TCP header.  You can only use this on TCP SYN or SYN/ACK packets, since the MSS is only negotiated during the TCP handshake at connection startup time.
+.TP
+.BI "[!] "--mss " "value[:value]"
+Match a given TCP MSS value or range.
index 909ca81..dcf2dc6 100644 (file)
@@ -3,6 +3,7 @@
 #include <netdb.h>
 #include <string.h>
 #include <stdlib.h>
+#include <stddef.h> /* for 'offsetof' */
 #include <getopt.h>
 
 #include <iptables.h>
@@ -17,10 +18,27 @@ help(void)
 {
        printf(
 "TIME v%s options:\n"
-" --timestart value --timestop value --days listofdays\n"
-"          timestart value : HH:MM\n"
-"          timestop  value : HH:MM\n"
-"          listofdays value: a list of days to apply -> ie. Mon,Tue,Wed,Thu,Fri. Case sensitive\n",
+" [ --timestart value ] [ --timestop value] [ --days listofdays ] [ --datestart value ] [ --datestop value ]\n"
+"          timestart value : HH:MM (default 00:00)\n"
+"          timestop  value : HH:MM (default 23:59)\n"
+"                            Note: daylight savings time changes are not tracked\n"
+"          listofdays value: a list of days to apply\n"
+"                            from Mon,Tue,Wed,Thu,Fri,Sat,Sun\n"
+"                            Coma speparated, no space, case sensitive.\n"
+"                            Defaults to all days.\n"
+"          datestart value : YYYY[:MM[:DD[:hh[:mm[:ss]]]]]\n"
+"                            If any of month, day, hour, minute or second is\n"
+"                            not specified, then defaults to their smallest\n"
+"                            1900 <= YYYY < 2037\n"
+"                               1 <= MM <= 12\n"
+"                               1 <= DD <= 31\n"
+"                               0 <= hh <= 23\n"
+"                               0 <= mm <= 59\n"
+"                               0 <= ss <= 59\n"
+"          datestop  value : YYYY[:MM[:DD[:hh[:mm[:ss]]]]]\n"
+"                            If the whole option is ommited, default to never stop\n"
+"                            If any of month, day, hour, minute or second is\n"
+"                            not specified, then default to their smallest\n",
 IPTABLES_VERSION);
 }
 
@@ -28,6 +46,8 @@ static struct option opts[] = {
        { "timestart", 1, 0, '1' },
        { "timestop", 1, 0, '2' },
        { "days", 1, 0, '3'},
+       { "datestart", 1, 0, '4' },
+       { "datestop", 1, 0, '5' },
        {0}
 };
 
@@ -35,9 +55,16 @@ static struct option opts[] = {
 static void
 init(struct ipt_entry_match *m, unsigned int *nfcache)
 {
-       /* caching not yet implemented */
-        *nfcache |= NFC_UNKNOWN;
+       struct ipt_time_info *info = (struct ipt_time_info *)m->data;
        globaldays = 0;
+        /* By default, we match on everyday */
+       info->days_match = 127;
+       /* By default, we match on every hour:min of the day */
+       info->time_start = 0;
+       info->time_stop  = 1439;  /* (23*60+59 = 1439 */
+       /* By default, we don't have any date-begin or date-end boundaries */
+       info->date_start = 0;
+       info->date_stop  = LONG_MAX;
 }
 
 /**
@@ -77,36 +104,45 @@ split_time(char **part1, char **part2, const char *str_2_parse)
        return 1;
 }
 
+static int
+parse_number(char *str, int num_min, int num_max, int *number)
+{
+       /* if the number starts with 0, replace it with a space else
+       string_to_number() will interpret it as octal !! */
+       if (strlen(str) == 0)
+               return 0;
+
+       if ((str[0] == '0') && (str[1] != '\0'))
+               str[0] = ' ';
+
+       return string_to_number(str, num_min, num_max, number);
+}
+
 static void
-parse_time_string(unsigned int *hour, unsigned int *minute, const char *time)
+parse_time_string(int *hour, int *minute, const char *time)
 {
        char *hours;
        char *minutes;
-
        hours = (char *)malloc(3);
        minutes = (char *)malloc(3);
-       bzero((void *)hours, 3);
-       bzero((void *)minutes, 3);
+       memset(hours, 0, 3);
+       memset(minutes, 0, 3);
 
-       if (split_time(&hours, &minutes, time) == 1)
+       if (split_time((char **)&hours, (char **)&minutes, time) == 1)
        {
-                /* if the number starts with 0, replace it with a space else
-                   this string_to_number will interpret it as octal !! */
-                if ((hours[0] == '0') && (hours[1] != '\0'))
-                       hours[0] = ' ';
-               if ((minutes[0] == '0') && (minutes[1] != '\0'))
-                       minutes[0] = ' ';
-
-               if((string_to_number(hours, 0, 23, hour) == -1) ||
-                       (string_to_number(minutes, 0, 59, minute) == -1)) {
-                       *hour = *minute = (-1);
+               *hour = 0;
+               *minute = 0;
+               if ((parse_number((char *)hours, 0, 23, hour) != -1) &&
+                   (parse_number((char *)minutes, 0, 59, minute) != -1))
+               {
+                       free(hours);
+                       free(minutes);
+                       return;
                }
        }
-       if ((*hour != (-1)) && (*minute != (-1))) {
-               free(hours);
-               free(minutes);
-               return;
-       }
+
+       free(hours);
+       free(minutes);
 
        /* If we are here, there was a problem ..*/
        exit_error(PARAMETER_PROBLEM,
@@ -160,10 +196,139 @@ parse_days_string(int *days, const char *daystring)
        }
 }
 
+static int
+parse_date_field(const char *str_to_parse, int str_to_parse_s, int start_pos,
+                 char *dest, int *next_pos)
+{
+       unsigned char found_value = 0;
+       unsigned char found_column = 0;
+       int i;
+
+       for (i=0; i<2; i++)
+       {
+               if ((i+start_pos) >= str_to_parse_s) /* don't exit boundaries of the string..  */
+                       break;
+               if (str_to_parse[i+start_pos] == ':')
+                       found_column = 1;
+               else
+               {
+                       found_value = 1;
+                       dest[i] = str_to_parse[i+start_pos];
+               }
+       }
+       if (found_value == 0)
+               return 0;
+       *next_pos = i + start_pos;
+       if (found_column == 0)
+               ++(*next_pos);
+       return 1;
+}
+
+static int
+split_date(char *year, char *month,  char *day,
+           char *hour, char *minute, char *second,
+           const char *str_to_parse)
+{
+        int i;
+        unsigned char found_column = 0;
+       int str_to_parse_s = strlen(str_to_parse);
+
+        /* Check the length of the string */
+        if ((str_to_parse_s > 19) ||  /* YYYY:MM:DD:HH:MM:SS */
+            (str_to_parse_s < 4))     /* YYYY*/
+                return 0;
+
+       /* Clear the buffers */
+        memset(year, 0, 4);
+       memset(month, 0, 2);
+       memset(day, 0, 2);
+       memset(hour, 0, 2);
+       memset(minute, 0, 2);
+       memset(second, 0, 2);
+
+       /* parse the year YYYY */
+       found_column = 0;
+       for (i=0; i<5; i++)
+       {
+               if (i >= str_to_parse_s)
+                       break;
+               if (str_to_parse[i] == ':')
+               {
+                       found_column = 1;
+                       break;
+               }
+               else
+                       year[i] = str_to_parse[i];
+       }
+       if (found_column == 1)
+               ++i;
+
+       /* parse the month if it exists */
+       if (! parse_date_field(str_to_parse, str_to_parse_s, i, month, &i))
+               return 1;
+
+       if (! parse_date_field(str_to_parse, str_to_parse_s, i, day, &i))
+               return 1;
+
+       if (! parse_date_field(str_to_parse, str_to_parse_s, i, hour, &i))
+               return 1;
+
+       if (! parse_date_field(str_to_parse, str_to_parse_s, i, minute, &i))
+               return 1;
+
+       parse_date_field(str_to_parse, str_to_parse_s, i, second, &i);
+
+        /* if we are here, format should be ok. */
+        return 1;
+}
+
+static time_t
+parse_date_string(const char *str_to_parse)
+{
+       char year[5];
+       char month[3];
+       char day[3];
+       char hour[3];
+       char minute[3];
+       char second[3];
+       struct tm t;
+       time_t temp_time;
+
+       memset(year, 0, 5);
+       memset(month, 0, 3);
+       memset(day, 0, 3);
+       memset(hour, 0, 3);
+       memset(minute, 0, 3);
+       memset(second, 0, 3);
+
+        if (split_date(year, month, day, hour, minute, second, str_to_parse) == 1)
+        {
+               memset((void *)&t, 0, sizeof(struct tm));
+               t.tm_isdst = -1;
+               t.tm_mday = 1;
+               if (!((parse_number(year, 1900, 2037, &(t.tm_year)) == -1) ||
+                     (parse_number(month, 1, 12, &(t.tm_mon)) == -1) ||
+                     (parse_number(day, 1, 31, &(t.tm_mday)) == -1) ||
+                     (parse_number(hour, 0, 9999, &(t.tm_hour)) == -1) ||
+                     (parse_number(minute, 0, 59, &(t.tm_min)) == -1) ||
+                     (parse_number(second, 0, 59, &(t.tm_sec)) == -1)))
+               {
+                       t.tm_year -= 1900;
+                       --(t.tm_mon);
+                       temp_time = mktime(&t);
+                       if (temp_time != -1)
+                               return temp_time;
+               }
+       }
+       exit_error(PARAMETER_PROBLEM,
+                  "invalid date `%s' specified, should be YYYY[:MM[:DD[:hh[:mm[:ss]]]]] format", str_to_parse);
+}
+
 #define IPT_TIME_START 0x01
 #define IPT_TIME_STOP  0x02
 #define IPT_TIME_DAYS  0x04
-
+#define IPT_DATE_START 0x08
+#define IPT_DATE_STOP  0x10
 
 /* Function which parses command options; returns true if it
    ate an option */
@@ -175,6 +340,7 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 {
        struct ipt_time_info *timeinfo = (struct ipt_time_info *)(*match)->data;
        int hours, minutes;
+       time_t temp_date;
 
        switch (c)
        {
@@ -215,19 +381,43 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                timeinfo->days_match = globaldays;
                *flags |= IPT_TIME_DAYS;
                break;
+
+               /* datestart */
+       case '4':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "unexpected '!' with --datestart");
+               if (*flags & IPT_DATE_START)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --datestart twice");
+               temp_date = parse_date_string(optarg);
+               timeinfo->date_start = temp_date;
+               *flags |= IPT_DATE_START;
+               break;
+
+               /* datestop*/
+       case '5':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "unexpected '!' with --datestop");
+               if (*flags & IPT_DATE_STOP)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --datestop twice");
+               temp_date = parse_date_string(optarg);
+               timeinfo->date_stop = temp_date;
+               *flags |= IPT_DATE_STOP;
+               break;
        default:
                return 0;
        }
        return 1;
 }
 
-/* Final check; must have specified --timestart --timestop --days. */
+/* Final check */
 static void
 final_check(unsigned int flags)
 {
-       if (flags != (IPT_TIME_START | IPT_TIME_STOP | IPT_TIME_DAYS))
-               exit_error(PARAMETER_PROBLEM,
-                          "TIME match: You must specify `--timestart --timestop and --days'");
+       /* Nothing to do */
 }
 
 
@@ -248,6 +438,7 @@ print_days(int daynum)
                        ++nbdays;
                }
        }
+       printf(" ");
 }
 
 static void
@@ -257,6 +448,23 @@ divide_time(int fulltime, int *hours, int *minutes)
        *minutes = fulltime % 60;
 }
 
+static void
+print_date(time_t date, char *command)
+{
+       struct tm *t;
+
+       /* If it's default value, don't print..*/
+       if (((date == 0) || (date == LONG_MAX)) && (command != NULL))
+               return;
+       t = localtime(&date);
+       if (command != NULL)
+               printf("%s %d:%d:%d:%d:%d:%d ", command, (t->tm_year + 1900), (t->tm_mon + 1),
+                       t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+        else
+               printf("%d-%d-%d %d:%d:%d ", (t->tm_year + 1900), (t->tm_mon + 1),
+                       t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+}
+
 /* Prints out the matchinfo. */
 static void
 print(const struct ipt_ip *ip,
@@ -268,11 +476,26 @@ print(const struct ipt_ip *ip,
 
        divide_time(time->time_start, &hour_start, &minute_start);
        divide_time(time->time_stop, &hour_stop, &minute_stop);
-       printf(" TIME from %d:%d to %d:%d on ",
-              hour_start, minute_start,
-              hour_stop, minute_stop);
-       print_days(time->days_match);
-       printf(" ");
+       printf("TIME ");
+       if (time->time_start != 0)
+               printf("from %d:%d ", hour_start, minute_start);
+       if (time->time_stop != 1439) /* 23*60+59 = 1439 */
+               printf("to %d:%d ", hour_stop, minute_stop);
+       printf("on ");
+       if (time->days_match == 127)
+               printf("all days ");
+       else
+               print_days(time->days_match);
+       if (time->date_start != 0)
+       {
+               printf("starting from ");
+               print_date(time->date_start, NULL);
+       }
+       if (time->date_stop != LONG_MAX)
+       {
+               printf("until date ");
+               print_date(time->date_stop, NULL);
+       }
 }
 
 /* Saves the data in parsable form to stdout. */
@@ -284,27 +507,40 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 
        divide_time(time->time_start, &hour_start, &minute_start);
        divide_time(time->time_stop, &hour_stop, &minute_stop);
-       printf(" --timestart %.2d:%.2d --timestop %.2d:%.2d --days ",
-              hour_start, minute_start,
-              hour_stop, minute_stop);
-       print_days(time->days_match);
-       printf(" ");
+       if (time->time_start != 0)
+               printf("--timestart %.2d:%.2d ",
+                       hour_start, minute_start);
+       
+       if (time->time_stop != 1439) /* 23*60+59 = 1439 */
+               printf("--timestop %.2d:%.2d ",
+                       hour_stop, minute_stop);
+       
+       if (time->days_match != 127)
+       {
+               printf("--days ");
+               print_days(time->days_match);
+               printf(" ");
+       }
+       print_date(time->date_start, "--datestart");
+       print_date(time->date_stop, "--datestop");
 }
 
+/* have to use offsetof() instead of IPT_ALIGN(), since kerneltime must not
+ * be compared when user deletes rule with '-D' */
 static
-struct iptables_match timestruct
-= { NULL,
-    "time",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_time_info)),
-    IPT_ALIGN(sizeof(struct ipt_time_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct iptables_match timestruct = {
+       .next           = NULL,
+       .name           = "time",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_time_info)),
+       .userspacesize  = offsetof(struct ipt_time_info, kerneltime),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_time.man b/extensions/libipt_time.man
new file mode 100644 (file)
index 0000000..94b4053
--- /dev/null
@@ -0,0 +1,16 @@
+This matches if the packet arrival time/date is within a given range. All options are facultative.
+.TP
+.BI " --timestart " "value"
+Match only if it is after `value' (Inclusive, format: HH:MM ; default 00:00).
+.TP
+.BI "--timestop  " "value"
+Match only if it is before `value' (Inclusive, format: HH:MM ; default 23:59).
+.TP
+.BI "--days " "listofdays"
+Match only if today is one of the given days. (format: Mon,Tue,Wed,Thu,Fri,Sat,Sun ; default everyday)
+.TP
+.BI "--datestart " "date"
+Match only if it is after `date' (Inclusive, format: YYYY[:MM[:DD[:hh[:mm[:ss]]]]] ; h,m,s start from 0 ; default to 1970)
+.TP
+.BI "--datestop " "date"
+Match only if it is before `date' (Inclusive, format: YYYY[:MM[:DD[:hh[:mm[:ss]]]]] ; h,m,s start from 0 ; default to 2037)
index 6786911..7a10a50 100644 (file)
@@ -47,13 +47,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_IP_TOS;
-}
-
 static void
 parse_tos(const unsigned char *s, struct ipt_tos_info *info)
 {
@@ -91,6 +84,11 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch (c) {
        case '1':
+               /* Ensure that `--tos' haven't been used yet. */
+               if (*flags == 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                       "tos match: only use --tos once!");
+
                check_inverse(optarg, &invert, &optind, 0);
                parse_tos(argv[optind-1], tosinfo);
                if (invert)
@@ -154,20 +152,18 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_tos(info->tos, 0);
 }
 
-static
-struct iptables_match tos
-= { NULL,
-    "tos",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_tos_info)),
-    IPT_ALIGN(sizeof(struct ipt_tos_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+static struct iptables_match tos = { 
+       .next           = NULL,
+       .name           = "tos",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tos_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tos_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_tos.man b/extensions/libipt_tos.man
new file mode 100644 (file)
index 0000000..c612b29
--- /dev/null
@@ -0,0 +1,9 @@
+This module matches the 8 bits of Type of Service field in the IP
+header (ie. including the precedence bits).
+.TP
+.BI "--tos " "tos"
+The argument is either a standard name, (use
+.br
+ iptables -m tos -h
+.br
+to see the list), or a numeric value to match.
index 4507fe9..2391986 100644 (file)
@@ -1,7 +1,7 @@
 /* Shared library add-on to iptables to add TTL matching support 
  * (C) 2000 by Harald Welte <laforge@gnumonks.org>
  *
- * $Id: libipt_ttl.c,v 1.6 2002/05/29 13:08:16 laforge Exp $
+ * $Id: libipt_ttl.c 3687 2005-02-14 13:13:04Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kaber/emailAddress=kaber@netfilter.org $
  *
  * This program is released under the terms of GNU GPL */
 
@@ -24,29 +24,19 @@ static void help(void)
 , IPTABLES_VERSION);
 }
 
-static void init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* caching not yet implemented */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 static int parse(int c, char **argv, int invert, unsigned int *flags,
                const struct ipt_entry *entry, unsigned int *nfcache,
                struct ipt_entry_match **match)
 {
        struct ipt_ttl_info *info = (struct ipt_ttl_info *) (*match)->data;
-       u_int8_t value;
+       int value;
 
        check_inverse(optarg, &invert, &optind, 0);
-       value = atoi(argv[optind-1]);
-
-       if (*flags) 
-               exit_error(PARAMETER_PROBLEM, 
-                               "Can't specify TTL option twice");
 
-       if (!optarg)
+       if (string_to_number(optarg, 0, 255, &value) == -1)
                exit_error(PARAMETER_PROBLEM,
-                               "ttl: You must specify a value");
+                          "ttl: Expected value between 0 and 255");
+
        switch (c) {
                case '2':
                        if (invert)
@@ -56,8 +46,6 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
 
                        /* is 0 allowed? */
                        info->ttl = value;
-                       *flags = 1;
-
                        break;
                case '3':
                        if (invert) 
@@ -66,8 +54,6 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
 
                        info->mode = IPT_TTL_LT;
                        info->ttl = value;
-                       *flags = 1;
-
                        break;
                case '4':
                        if (invert)
@@ -76,14 +62,17 @@ static int parse(int c, char **argv, int invert, unsigned int *flags,
 
                        info->mode = IPT_TTL_GT;
                        info->ttl = value;
-                       *flags = 1;
-
                        break;
                default:
                        return 0;
 
        }
 
+       if (*flags) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify TTL option twice");
+       *flags = 1;
+
        return 1;
 }
 
@@ -154,20 +143,18 @@ static struct option opts[] = {
        { 0 }
 };
 
-static
-struct iptables_match ttl = {
-       NULL,
-       "ttl",
-       IPTABLES_VERSION,
-       IPT_ALIGN(sizeof(struct ipt_ttl_info)),
-       IPT_ALIGN(sizeof(struct ipt_ttl_info)),
-       &help,
-       &init,
-       &parse,
-       &final_check,
-       &print,
-       &save,
-       opts
+static struct iptables_match ttl = {
+       .next           = NULL,
+       .name           = "ttl",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ttl_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ttl_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 
diff --git a/extensions/libipt_ttl.man b/extensions/libipt_ttl.man
new file mode 100644 (file)
index 0000000..f043c79
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches the time to live field in the IP header.
+.TP
+.BI "--ttl-eq " "ttl"
+Matches the given TTL value.
+.TP
+.BI "--ttl-gt " "ttl"
+Matches if TTL is greater than the given TTL value.
+.TP
+.BI "--ttl-lt " "ttl"
+Matches if TTL is less than the given TTL value.
index 788413c..7504510 100644 (file)
@@ -37,13 +37,6 @@ static struct option opts[] = {
        { 0 }
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* shared printing code */
 static void print_u32(struct ipt_u32 *data)
 {
@@ -85,7 +78,7 @@ u_int32_t parse_number(char **s, int pos)
        char *end;
        errno = 0;
 
-       number = strtol(*s, &end, 0);
+       number = strtoul(*s, &end, 0);
        if (end == *s)
                exit_error(PARAMETER_PROBLEM, 
                           "u32: at char %d expected number", pos);
@@ -250,19 +243,18 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
        print_u32((struct ipt_u32 *)match->data);
 }
 
-struct iptables_match u32
-= { NULL,
-    "u32",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_u32)),
-    IPT_ALIGN(sizeof(struct ipt_u32)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct iptables_match u32 = {
+       .next           = NULL,
+       .name           = "u32",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_u32)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_u32)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void
diff --git a/extensions/libipt_u32.man b/extensions/libipt_u32.man
new file mode 100644 (file)
index 0000000..7028bd5
--- /dev/null
@@ -0,0 +1,8 @@
+U32 allows you to extract quantities of up to 4 bytes from a packet,
+AND them with specified masks, shift them by specified amounts and
+test whether the results are in any of a set of specified ranges.
+The specification of what to extract is general enough to skip over
+headers with lengths stored in the packet, as in IP or TCP header
+lengths.
+
+Details and examples are in the kernel module source.
index ccea210..f45f364 100644 (file)
@@ -109,7 +109,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        udpinfo->invflags |= IPT_UDP_INV_SRCPT;
                *flags |= UDP_SRC_PORTS;
-               *nfcache |= NFC_IP_SRC_PT;
                break;
 
        case '2':
@@ -121,7 +120,6 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                if (invert)
                        udpinfo->invflags |= IPT_UDP_INV_DSTPT;
                *flags |= UDP_DST_PORTS;
-               *nfcache |= NFC_IP_DST_PT;
                break;
 
        default:
@@ -234,19 +232,19 @@ static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 }
 
 static
-struct iptables_match udp
-= { NULL,
-    "udp",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_udp)),
-    IPT_ALIGN(sizeof(struct ipt_udp)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct iptables_match udp = { 
+       .next           = NULL,
+       .name           = "udp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_udp)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_udp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void
diff --git a/extensions/libipt_udp.man b/extensions/libipt_udp.man
new file mode 100644 (file)
index 0000000..0408479
--- /dev/null
@@ -0,0 +1,14 @@
+These extensions are loaded if `--protocol udp' is specified.  It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
index 16cc0a5..7b9b3e4 100644 (file)
@@ -17,14 +17,6 @@ static struct option opts[] = {
        {0}
 };
 
-/* Initialize the match. */
-static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
-{
-       /* Can't cache this. */
-       *nfcache |= NFC_UNKNOWN;
-}
-
 /* Function which parses command options; returns true if it
    ate an option */
 static int
@@ -42,19 +34,18 @@ static void final_check(unsigned int flags)
 }
 
 static
-struct iptables_match unclean
-= { NULL,
-    "unclean",
-    IPTABLES_VERSION,
-    IPT_ALIGN(0),
-    IPT_ALIGN(0),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    NULL, /* print */
-    NULL, /* save */
-    opts
+struct iptables_match unclean = { 
+       .next           = NULL,
+       .name           = "unclean",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = NULL,
+       .save           = NULL,
+       .extra_opts     = opts
 };
 
 void _init(void)
diff --git a/extensions/libipt_unclean.man b/extensions/libipt_unclean.man
new file mode 100644 (file)
index 0000000..3fecd55
--- /dev/null
@@ -0,0 +1,2 @@
+This module takes no options, but attempts to match packets which seem
+malformed or unusual.  This is regarded as experimental.
index b098f81..81d99d8 100644 (file)
@@ -4,6 +4,17 @@
 #include "iptables_common.h"
 #include "libiptc/libip6tc.h"
 
+#ifndef IP6T_LIB_DIR
+#define IP6T_LIB_DIR "/usr/local/lib/iptables"
+#endif
+
+struct ip6tables_rule_match
+{
+       struct ip6tables_rule_match *next;
+
+       struct ip6tables_match *match;
+};
+
 /* Include file for additions: new matches and targets. */
 struct ip6tables_match
 {
@@ -50,7 +61,6 @@ struct ip6tables_match
        unsigned int option_offset;
        struct ip6t_entry_match *m;
        unsigned int mflags;
-       unsigned int used;
 #ifdef NO_SHARED_LIBS
        unsigned int loaded; /* simulate loading so options are merged properly */
 #endif
@@ -125,7 +135,9 @@ enum ip6t_tryload {
 };
 
 extern struct ip6tables_target *find_target(const char *name, enum ip6t_tryload);
-extern struct ip6tables_match *find_match(const char *name, enum ip6t_tryload);
+extern struct ip6tables_match *find_match(const char *name, enum ip6t_tryload, struct ip6tables_rule_match **match);
+
+extern void parse_interface(const char *arg, char *vianame, unsigned char *mask);
 
 extern int for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *), int verbose, int builtinstoo, ip6tc_handle_t *handle);
 extern int flush_entries(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle);
index 205984e..f0cad8d 100644 (file)
@@ -4,6 +4,33 @@
 #include "iptables_common.h"
 #include "libiptc/libiptc.h"
 
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/local/lib/iptables"
+#endif
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+
+#ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
+#define IPT_SO_GET_REVISION_MATCH      (IPT_BASE_CTL + 2)
+#define IPT_SO_GET_REVISION_TARGET     (IPT_BASE_CTL + 3)
+
+struct ipt_get_revision
+{
+       char name[IPT_FUNCTION_MAXNAMELEN-1];
+
+       u_int8_t revision;
+};
+#endif /* IPT_SO_GET_REVISION_MATCH   Old kernel source */
+
+struct iptables_rule_match
+{
+       struct iptables_rule_match *next;
+
+       struct iptables_match *match;
+};
+
 /* Include file for additions: new matches and targets. */
 struct iptables_match
 {
@@ -11,6 +38,9 @@ struct iptables_match
 
        ipt_chainlabel name;
 
+       /* Revision of match (0 by default). */
+       u_int8_t revision;
+
        const char *version;
 
        /* Size of match data. */
@@ -50,7 +80,6 @@ struct iptables_match
        unsigned int option_offset;
        struct ipt_entry_match *m;
        unsigned int mflags;
-       unsigned int used;
 #ifdef NO_SHARED_LIBS
        unsigned int loaded; /* simulate loading so options are merged properly */
 #endif
@@ -62,6 +91,9 @@ struct iptables_target
 
        ipt_chainlabel name;
 
+       /* Revision of target (0 by default). */
+       u_int8_t revision;
+
        const char *version;
 
        /* Size of target data. */
@@ -120,6 +152,7 @@ extern char *mask_to_dotted(const struct in_addr *mask);
 extern void parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
                       struct in_addr *maskp, unsigned int *naddrs);
 extern u_int16_t parse_protocol(const char *s);
+extern void parse_interface(const char *arg, char *vianame, unsigned char *mask);
 
 extern int do_command(int argc, char *argv[], char **table,
                      iptc_handle_t *handle);
@@ -134,7 +167,7 @@ enum ipt_tryload {
 };
 
 extern struct iptables_target *find_target(const char *name, enum ipt_tryload);
-extern struct iptables_match *find_match(const char *name, enum ipt_tryload);
+extern struct iptables_match *find_match(const char *name, enum ipt_tryload, struct iptables_rule_match **match);
 
 extern int delete_chain(const ipt_chainlabel chain, int verbose,
                        iptc_handle_t *handle);
index 25f23c3..db5e93f 100644 (file)
@@ -14,10 +14,19 @@ extern int string_to_number(const char *,
                            unsigned int, 
                            unsigned int,
                            unsigned int *);
+extern int string_to_number_l(const char *, 
+                           unsigned long int, 
+                           unsigned long int,
+                           unsigned long *);
+extern int string_to_number_ll(const char *, 
+                           unsigned long long int, 
+                           unsigned long long int,
+                           unsigned long long *);
 extern int iptables_insmod(const char *modname, const char *modprobe);
 void exit_error(enum exittype, char *, ...)__attribute__((noreturn,
                                                          format(printf,2,3)));
 extern const char *program_name, *program_version;
+extern char *lib_dir;
 
 #ifdef NO_SHARED_LIBS
 # ifdef _INIT
diff --git a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h
new file mode 100644 (file)
index 0000000..6f76060
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _IPT_CLUSTERIP_H_target
+#define _IPT_CLUSTERIP_H_target
+
+enum clusterip_hashmode {
+    CLUSTERIP_HASHMODE_SIP = 0,
+    CLUSTERIP_HASHMODE_SIP_SPT,
+    CLUSTERIP_HASHMODE_SIP_SPT_DPT,
+};
+
+#define CLUSTERIP_HASHMODE_MAX CLUSTERIP_HASHMODE_SIP_SPT_DPT
+
+#define CLUSTERIP_MAX_NODES 16
+
+#define CLUSTERIP_FLAG_NEW 0x00000001
+
+struct clusterip_config;
+
+struct ipt_clusterip_tgt_info {
+
+       u_int32_t flags;
+       
+       /* only relevant for new ones */
+       u_int8_t clustermac[6];
+       u_int16_t num_total_nodes;
+       u_int16_t num_local_nodes;
+       u_int16_t local_nodes[CLUSTERIP_MAX_NODES];
+       enum clusterip_hashmode hash_mode;
+       u_int32_t hash_initval;
+       
+#ifdef KERNEL_64_USERSPACE_32
+       u_int64_t config;
+#else
+       struct clusterip_config *config;
+#endif
+};
+
+#endif /*_IPT_CLUSTERIP_H_target*/
index b237a4f..0148539 100644 (file)
@@ -1,6 +1,15 @@
 #ifndef _IPT_CONNMARK_H_target
 #define _IPT_CONNMARK_H_target
 
+/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.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.
+ */
+
 enum {
        IPT_CONNMARK_SET = 0,
        IPT_CONNMARK_SAVE,
@@ -8,7 +17,13 @@ enum {
 };
 
 struct ipt_connmark_target_info {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+       unsigned long long mask;
+#else
        unsigned long mark;
+       unsigned long mask;
+#endif
        u_int8_t mode;
 };
 
diff --git a/include/linux/netfilter_ipv4/ipt_MARK.h b/include/linux/netfilter_ipv4/ipt_MARK.h
new file mode 100644 (file)
index 0000000..3694e48
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _IPT_MARK_H_target
+#define _IPT_MARK_H_target
+
+struct ipt_mark_target_info {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+#else
+       unsigned long mark;
+#endif
+};
+
+enum {
+       IPT_MARK_SET=0,
+       IPT_MARK_AND,
+       IPT_MARK_OR
+};
+
+struct ipt_mark_target_info_v1 {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+#else
+       unsigned long mark;
+#endif
+       u_int8_t mode;
+};
+
+#endif /*_IPT_MARK_H_target*/
index 428b213..89ba22f 100644 (file)
@@ -8,12 +8,13 @@
 struct ipt_same_info
 {
        unsigned char info;
-
-       unsigned int rangesize;
-
-       unsigned int ipnum;
-
+       u_int32_t rangesize;
+       u_int32_t ipnum;
+#ifdef KERNEL_64_USERSPACE_32
+       u_int64_t placeholder;
+#else
        u_int32_t *iparray;
+#endif
 
        /* hangs off end. */
        struct ip_nat_range range[IPT_SAME_MAX_RANGE];
index 86a99ee..f267ab8 100644 (file)
@@ -1,4 +1,4 @@
-/* Header file for IP tables userspace logging, Version 1.8 
+/* Header file for IP tables userspace logging, Version 1.8
  *
  * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
  * 
@@ -11,6 +11,9 @@
 #define NETLINK_NFLOG  5
 #endif
 
+#define ULOG_DEFAULT_NLGROUP   1
+#define ULOG_DEFAULT_QTHRESHOLD        1
+
 #define ULOG_MAC_LEN   80
 #define ULOG_PREFIX_LEN        32
 
 /* private data structure for each rule with a ULOG target */
 struct ipt_ulog_info {
        unsigned int nl_group;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long copy_range;
+       unsigned long long qthreshold;
+#else
        size_t copy_range;
        size_t qthreshold;
+#endif
        char prefix[ULOG_PREFIX_LEN];
 };
 
diff --git a/include/linux/netfilter_ipv4/ipt_addrtype.h b/include/linux/netfilter_ipv4/ipt_addrtype.h
new file mode 100644 (file)
index 0000000..166ed01
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _IPT_ADDRTYPE_H
+#define _IPT_ADDRTYPE_H
+
+struct ipt_addrtype_info {
+       u_int16_t       source;         /* source-type mask */
+       u_int16_t       dest;           /* dest-type mask */
+       u_int32_t       invert_source;
+       u_int32_t       invert_dest;
+};
+
+#endif
diff --git a/include/linux/netfilter_ipv4/ipt_comment.h b/include/linux/netfilter_ipv4/ipt_comment.h
new file mode 100644 (file)
index 0000000..85c1123
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _IPT_COMMENT_H
+#define _IPT_COMMENT_H
+
+#define IPT_MAX_COMMENT_LEN 256
+
+struct ipt_comment_info {
+       unsigned char comment[IPT_MAX_COMMENT_LEN];
+};
+
+#endif /* _IPT_COMMENT_H */
index e19742a..151e268 100644 (file)
@@ -1,8 +1,21 @@
 #ifndef _IPT_CONNMARK_H
 #define _IPT_CONNMARK_H
 
+/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.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.
+ */
+
 struct ipt_connmark_info {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark, mask;
+#else
        unsigned long mark, mask;
+#endif
        u_int8_t invert;
 };
 
index eb97456..eba410d 100644 (file)
@@ -5,11 +5,29 @@
 #ifndef _IPT_CONNTRACK_H
 #define _IPT_CONNTRACK_H
 
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+/* backwards compatibility crap. only exists in userspace - HW */
+#include <linux/version.h>
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c))
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+#define IPS_EXPECTED   (1 << 0)
+#define IPS_SEEN_REPLY (1 << 1)
+#define IPS_ASSURED    (1 << 2)
+#define IP_CT_DIR_ORIGINAL     0
+#define IP_CT_DIR_REPLY                1
+#define IP_CT_DIR_MAX          2
+#endif
+
 #define IPT_CONNTRACK_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1))
 #define IPT_CONNTRACK_STATE_INVALID (1 << 0)
 
 #define IPT_CONNTRACK_STATE_SNAT (1 << (IP_CT_NUMBER + 1))
 #define IPT_CONNTRACK_STATE_DNAT (1 << (IP_CT_NUMBER + 2))
+#define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
 
 /* flags, invflags: */
 #define IPT_CONNTRACK_STATE    0x01
 #define IPT_CONNTRACK_STATUS   0x40
 #define IPT_CONNTRACK_EXPIRES  0x80
 
+/* This is exposed to userspace, so remains frozen in time. */
+struct ip_conntrack_old_tuple
+{
+       struct {
+               u_int32_t ip;
+               union {
+                       u_int16_t all;
+               } u;
+       } src;
+
+       struct {
+               u_int32_t ip;
+               union {
+                       u_int16_t all;
+               } u;
+
+               /* The protocol. */
+               u_int16_t protonum;
+       } dst;
+};
+
 struct ipt_conntrack_info
 {
        unsigned int statemask, statusmask;
 
-       struct ip_conntrack_tuple tuple[IP_CT_DIR_MAX];
+       struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX];
        struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX];
 
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long expires_min, expires_max;
+#else
        unsigned long expires_min, expires_max;
+#endif
 
        /* Flags word */
        u_int8_t flags;
@@ -36,4 +79,3 @@ struct ipt_conntrack_info
        u_int8_t invflags;
 };
 #endif /*_IPT_CONNTRACK_H*/
-
diff --git a/include/linux/netfilter_ipv4/ipt_dstlimit.h b/include/linux/netfilter_ipv4/ipt_dstlimit.h
new file mode 100644 (file)
index 0000000..1a88f6b
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _IPT_DSTLIMIT_H
+#define _IPT_DSTLIMIT_H
+
+/* timings are in milliseconds. */
+#define IPT_DSTLIMIT_SCALE 10000
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+
+/* details of this structure hidden by the implementation */
+struct ipt_dstlimit_htable;
+
+#define IPT_DSTLIMIT_HASH_DIP  0x0001
+#define IPT_DSTLIMIT_HASH_DPT  0x0002
+#define IPT_DSTLIMIT_HASH_SIP  0x0004
+
+struct dstlimit_cfg {
+       u_int32_t mode;   /* bitmask of IPT_DSTLIMIT_HASH_* */
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+       /* user specified */
+       u_int32_t size;         /* how many buckets */
+       u_int32_t max;          /* max number of entries */
+       u_int32_t gc_interval;  /* gc interval */
+       u_int32_t expire;       /* when do entries expire? */
+};
+
+struct ipt_dstlimit_info {
+       char name [IFNAMSIZ];           /* name */
+       struct dstlimit_cfg cfg;
+       struct ipt_dstlimit_htable *hinfo;
+
+       /* Used internally by the kernel */
+       union {
+               void *ptr;
+               struct ipt_dstlimit_info *master;
+       } u;
+};
+#endif /*_IPT_DSTLIMIT_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_hashlimit.h b/include/linux/netfilter_ipv4/ipt_hashlimit.h
new file mode 100644 (file)
index 0000000..ac2cb64
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _IPT_HASHLIMIT_H
+#define _IPT_HASHLIMIT_H
+
+/* timings are in milliseconds. */
+#define IPT_HASHLIMIT_SCALE 10000
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+
+/* details of this structure hidden by the implementation */
+struct ipt_hashlimit_htable;
+
+#define IPT_HASHLIMIT_HASH_DIP 0x0001
+#define IPT_HASHLIMIT_HASH_DPT 0x0002
+#define IPT_HASHLIMIT_HASH_SIP 0x0004
+#define IPT_HASHLIMIT_HASH_SPT 0x0008
+
+struct hashlimit_cfg {
+       u_int32_t mode;   /* bitmask of IPT_HASHLIMIT_HASH_* */
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+       /* user specified */
+       u_int32_t size;         /* how many buckets */
+       u_int32_t max;          /* max number of entries */
+       u_int32_t gc_interval;  /* gc interval */
+       u_int32_t expire;       /* when do entries expire? */
+};
+
+struct ipt_hashlimit_info {
+       char name [IFNAMSIZ];           /* name */
+       struct hashlimit_cfg cfg;
+       struct ipt_hashlimit_htable *hinfo;
+
+       /* Used internally by the kernel */
+       union {
+               void *ptr;
+               struct ipt_hashlimit_info *master;
+       } u;
+};
+#endif /*_IPT_HASHLIMIT_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_limit.h b/include/linux/netfilter_ipv4/ipt_limit.h
new file mode 100644 (file)
index 0000000..e2fb166
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef _IPT_RATE_H
+#define _IPT_RATE_H
+
+/* timings are in milliseconds. */
+#define IPT_LIMIT_SCALE 10000
+
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+struct ipt_rateinfo {
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+#ifdef KERNEL_64_USERSPACE_32
+       u_int64_t prev;
+       u_int64_t placeholder;
+#else
+       /* Used internally by the kernel */
+       unsigned long prev;
+       /* Ugly, ugly fucker. */
+       struct ipt_rateinfo *master;
+#endif
+
+       u_int32_t credit;
+       u_int32_t credit_cap, cost;
+};
+#endif /*_IPT_RATE_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_mark.h b/include/linux/netfilter_ipv4/ipt_mark.h
new file mode 100644 (file)
index 0000000..b9e79fd
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _IPT_MARK_H
+#define _IPT_MARK_H
+
+struct ipt_mark_info {
+#ifdef KERNEL_64_USERSPACE_32
+    unsigned long long mark, mask;
+#else
+    unsigned long mark, mask;
+#endif
+    u_int8_t invert;
+};
+
+#endif /*_IPT_MARK_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_multiport.h b/include/linux/netfilter_ipv4/ipt_multiport.h
new file mode 100644 (file)
index 0000000..4b95d13
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _IPT_MULTIPORT_H
+#define _IPT_MULTIPORT_H
+
+enum ipt_multiport_flags
+{
+       IPT_MULTIPORT_SOURCE,
+       IPT_MULTIPORT_DESTINATION,
+       IPT_MULTIPORT_EITHER
+};
+
+#define IPT_MULTI_PORTS        15
+
+/* Must fit inside union ipt_matchinfo: 16 bytes */
+struct ipt_multiport
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[IPT_MULTI_PORTS];       /* Ports */
+};
+
+struct ipt_multiport_v1
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[IPT_MULTI_PORTS];       /* Ports */
+       u_int8_t pflags[IPT_MULTI_PORTS];       /* Port flags */
+       u_int8_t invert;                        /* Invert flag */
+};
+#endif /*_IPT_MULTIPORT_H*/
index 4b2cafc..e93a9ec 100644 (file)
-/* iptables module for matching the SCTP header
- *
- * (C) 2003 Harald Welte <laforge@gnumonks.org>
- *
- * This software is distributed under GNU GPL v2, 1991
- *
- * $Id: ipt_sctp.h,v 1.1 2003/05/03 18:05:58 laforge Exp $
- */
-#ifndef _IPT_SCTP_H
-#define _IPT_SCTP_H
+#ifndef _IPT_SCTP_H_
+#define _IPT_SCTP_H_
+
+#define IPT_SCTP_SRC_PORTS             0x01
+#define IPT_SCTP_DEST_PORTS            0x02
+#define IPT_SCTP_CHUNK_TYPES           0x04
+
+#define IPT_SCTP_VALID_FLAGS           0x07
+
+#define ELEMCOUNT(x) (sizeof(x)/sizeof(x[0]))
+
+
+struct ipt_sctp_flag_info {
+       u_int8_t chunktype;
+       u_int8_t flag;
+       u_int8_t flag_mask;
+};
+
+#define IPT_NUM_SCTP_FLAGS     4
 
 struct ipt_sctp_info {
-       u_int16_t spts[2];                      /* Souce port range */
-       u_int16_t dpts[2];                      /* Destination port range */
-       u_int32_t chunks;                       /* chunks to be matched */
-       u_int32_t chunk_mask;                   /* chunk mask to be matched */
-       u_int8_t invflags;                      /* Inverse flags */
+       u_int16_t dpts[2];  /* Min, Max */
+       u_int16_t spts[2];  /* Min, Max */
+
+       u_int32_t chunkmap[256 / sizeof (u_int32_t)];  /* Bit mask of chunks to be matched according to RFC 2960 */
+
+#define SCTP_CHUNK_MATCH_ANY   0x01  /* Match if any of the chunk types are present */
+#define SCTP_CHUNK_MATCH_ALL   0x02  /* Match if all of the chunk types are present */
+#define SCTP_CHUNK_MATCH_ONLY  0x04  /* Match if these are the only chunk types present */
+
+       u_int32_t chunk_match_type;
+       struct ipt_sctp_flag_info flag_info[IPT_NUM_SCTP_FLAGS];
+       int flag_count;
+
+       u_int32_t flags;
+       u_int32_t invflags;
 };
 
-#define IPT_SCTP_INV_SRCPT     0x01    /* Invert the sense of source ports */
-#define IPT_SCTP_INV_DSTPT     0x02    /* Invert the sense of dest ports */
-#define IPT_SCTP_INV_CHUNKS    0x03    /* Invert the sense of chunks */
-#define IPT_SCTP_INV_MASK      0x03    /* All possible flags */
+#define bytes(type) (sizeof(type) * 8)
+
+#define SCTP_CHUNKMAP_SET(chunkmap, type)              \
+       do {                                            \
+               chunkmap[type / bytes(u_int32_t)] |=    \
+                       1 << (type % bytes(u_int32_t)); \
+       } while (0)
+
+#define SCTP_CHUNKMAP_CLEAR(chunkmap, type)                    \
+       do {                                                    \
+               chunkmap[type / bytes(u_int32_t)] &=            \
+                       ~(1 << (type % bytes(u_int32_t)));      \
+       } while (0)
+
+#define SCTP_CHUNKMAP_IS_SET(chunkmap, type)                   \
+({                                                             \
+       (chunkmap[type / bytes (u_int32_t)] &                   \
+               (1 << (type % bytes (u_int32_t)))) ? 1: 0;      \
+})
+
+#define SCTP_CHUNKMAP_RESET(chunkmap)                          \
+       do {                                                    \
+               int i;                                          \
+               for (i = 0; i < ELEMCOUNT(chunkmap); i++)       \
+                       chunkmap[i] = 0;                        \
+       } while (0)
+
+#define SCTP_CHUNKMAP_SET_ALL(chunkmap)                        \
+       do {                                                    \
+               int i;                                          \
+               for (i = 0; i < ELEMCOUNT(chunkmap); i++)       \
+                       chunkmap[i] = ~0;                       \
+       } while (0)
+
+#define SCTP_CHUNKMAP_COPY(destmap, srcmap)                    \
+       do {                                                    \
+               int i;                                          \
+               for (i = 0; i < ELEMCOUNT(chunkmap); i++)       \
+                       destmap[i] = srcmap[i];                 \
+       } while (0)
+
+#define SCTP_CHUNKMAP_IS_CLEAR(chunkmap)               \
+({                                                     \
+       int i;                                          \
+       int flag = 1;                                   \
+       for (i = 0; i < ELEMCOUNT(chunkmap); i++) {     \
+               if (chunkmap[i]) {                      \
+                       flag = 0;                       \
+                       break;                          \
+               }                                       \
+       }                                               \
+        flag;                                          \
+})
+
+#define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)             \
+({                                                     \
+       int i;                                          \
+       int flag = 1;                                   \
+       for (i = 0; i < ELEMCOUNT(chunkmap); i++) {     \
+               if (chunkmap[i] != ~0) {                \
+                       flag = 0;                       \
+                               break;                  \
+               }                                       \
+       }                                               \
+        flag;                                          \
+})
+
+#endif /* _IPT_SCTP_H_ */
 
-#endif /* _IPT_SCTP_H */
diff --git a/include/linux/netfilter_ipv6/ip6t_MARK.h b/include/linux/netfilter_ipv6/ip6t_MARK.h
new file mode 100644 (file)
index 0000000..06949b8
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _IP6T_MARK_H_target
+#define _IP6T_MARK_H_target
+
+struct ip6t_mark_target_info {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+#else
+       unsigned long mark;
+#endif
+};
+
+#endif /*_IPT_MARK_H_target*/
diff --git a/include/linux/netfilter_ipv6/ip6t_limit.h b/include/linux/netfilter_ipv6/ip6t_limit.h
new file mode 100644 (file)
index 0000000..cd3e834
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _IP6T_RATE_H
+#define _IP6T_RATE_H
+
+/* timings are in milliseconds. */
+#define IP6T_LIMIT_SCALE 10000
+
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+struct ip6t_rateinfo {
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+#ifdef KERNEL_64_USERSPACE_32
+       u_int64_t prev;
+       u_int64_t placeholder;
+#else
+       /* Used internally by the kernel */
+       unsigned long prev;
+       /* Ugly, ugly fucker. */
+       struct ip6t_rateinfo *master;
+#endif
+       u_int32_t credit;
+       u_int32_t credit_cap, cost;
+};
+#endif /*_IPT_RATE_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_mark.h b/include/linux/netfilter_ipv6/ip6t_mark.h
new file mode 100644 (file)
index 0000000..7ede185
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _IP6T_MARK_H
+#define _IP6T_MARK_H
+
+struct ip6t_mark_info {
+#ifdef KERNEL_64_USERSPACE_32
+    unsigned long long mark, mask;
+#else
+    unsigned long mark, mask;
+#endif
+    u_int8_t invert;
+};
+
+#endif /*_IPT_MARK_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_physdev.h b/include/linux/netfilter_ipv6/ip6t_physdev.h
new file mode 100644 (file)
index 0000000..c234731
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _IP6T_PHYSDEV_H
+#define _IP6T_PHYSDEV_H
+
+#ifdef __KERNEL__
+#include <linux/if.h>
+#endif
+
+#define IP6T_PHYSDEV_OP_IN             0x01
+#define IP6T_PHYSDEV_OP_OUT            0x02
+#define IP6T_PHYSDEV_OP_BRIDGED                0x04
+#define IP6T_PHYSDEV_OP_ISIN           0x08
+#define IP6T_PHYSDEV_OP_ISOUT          0x10
+#define IP6T_PHYSDEV_OP_MASK           (0x20 - 1)
+
+struct ip6t_physdev_info {
+       char physindev[IFNAMSIZ];
+       char in_mask[IFNAMSIZ];
+       char physoutdev[IFNAMSIZ];
+       char out_mask[IFNAMSIZ];
+       u_int8_t invert;
+       u_int8_t bitmask;
+};
+
+#endif /*_IP6T_PHYSDEV_H*/
index 74b5c78..38aa2d6 100644 (file)
@@ -7,7 +7,7 @@
  *     Rusty Russell <rusty@linuxcare.com.au>
  * This code is distributed under the terms of GNU GPL v2
  *
- * $Id: ip6tables-restore.c,v 1.12 2003/05/02 15:30:11 laforge Exp $
+ * $Id: ip6tables-restore.c 3980 2005-06-12 15:54:15Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kaber/emailAddress=kaber@netfilter.org $
  */
 
 #include <getopt.h>
@@ -30,7 +30,8 @@ static int binary = 0, counters = 0, verbose = 0, noflush = 0;
 static struct option options[] = {
        { "binary", 0, 0, 'b' },
        { "counters", 0, 0, 'c' },
-       { "verbose", 1, 0, 'v' },
+       { "verbose", 0, 0, 'v' },
+       { "test", 0, 0, 't' },
        { "help", 0, 0, 'h' },
        { "noflush", 0, 0, 'n'},
        { "modprobe", 1, 0, 'M'},
@@ -41,10 +42,11 @@ 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] [-h]\n"
+       fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
                        "          [ --binary ]\n"
                        "          [ --counters ]\n"
                        "          [ --verbose ]\n"
+                       "          [ --test ]\n"
                        "          [ --help ]\n"
                        "          [ --noflush ]\n"
                        "          [ --modprobe=<command>]\n", name);
@@ -74,7 +76,7 @@ ip6tc_handle_t create_handle(const char *tablename, const char* modprobe)
 
 int parse_counters(char *string, struct ip6t_counters *ctr)
 {
-       return (sscanf(string, "[%llu:%llu]", &ctr->pcnt, &ctr->bcnt) == 2);
+       return (sscanf(string, "[%llu:%llu]", (unsigned long long *)&ctr->pcnt, (unsigned long long *)&ctr->bcnt) == 2);
 }
 
 /* global new argv and argc */
@@ -108,17 +110,21 @@ int main(int argc, char *argv[])
        char curtable[IP6T_TABLE_MAXNAMELEN + 1];
        FILE *in;
        const char *modprobe = 0;
-       int in_table = 0;
+       int in_table = 0, testing = 0;
 
        program_name = "ip6tables-restore";
        program_version = IPTABLES_VERSION;
        line = 0;
 
+       lib_dir = getenv("IP6TABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IP6T_LIB_DIR;
+
 #ifdef NO_SHARED_LIBS
        init_extensions();
 #endif
 
-       while ((c = getopt_long(argc, argv, "bcvhnM:", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) {
                switch (c) {
                        case 'b':
                                binary = 1;
@@ -129,6 +135,9 @@ int main(int argc, char *argv[])
                        case 'v':
                                verbose = 1;
                                break;
+                       case 't':
+                               testing = 1;
+                               break;
                        case 'h':
                                print_usage("ip6tables-restore",
                                            IPTABLES_VERSION);
@@ -158,16 +167,23 @@ int main(int argc, char *argv[])
        
        /* Grab standard input. */
        while (fgets(buffer, sizeof(buffer), in)) {
-               int ret;
+               int ret = 0;
 
                line++;
-               if (buffer[0] == '\n') continue;
+               if (buffer[0] == '\n')
+                       continue;
                else if (buffer[0] == '#') {
-                       if (verbose) fputs(buffer, stdout);
+                       if (verbose)
+                               fputs(buffer, stdout);
                        continue;
                } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
-                       DEBUGP("Calling commit\n");
-                       ret = ip6tc_commit(&handle);
+                       if (!testing) {
+                               DEBUGP("Calling commit\n");
+                               ret = ip6tc_commit(&handle);
+                       } else {
+                               DEBUGP("Not calling commit, testing\n");
+                               ret = 1;
+                       }
                        in_table = 0;
                } else if ((buffer[0] == '*') && (!in_table)) {
                        /* New table */
@@ -182,6 +198,7 @@ int main(int argc, char *argv[])
                                exit(1);
                        }
                        strncpy(curtable, table, IP6T_TABLE_MAXNAMELEN);
+                       curtable[IP6T_TABLE_MAXNAMELEN] = '\0';
 
                        if (handle)
                                ip6tc_free(&handle);
@@ -215,13 +232,22 @@ int main(int argc, char *argv[])
                                exit(1);
                        }
 
-                       if (!ip6tc_builtin(chain, handle)) {
-                               DEBUGP("Creating new chain '%s'\n", chain);
-                               if (!ip6tc_create_chain(chain, &handle))
-                                       exit_error(PARAMETER_PROBLEM,
-                                                  "error creating chain "
-                                                  "'%s':%s\n", chain,
-                                                  strerror(errno));
+                       if (ip6tc_builtin(chain, handle) <= 0) {
+                               if (noflush && ip6tc_is_chain(chain, handle)) {
+                                       DEBUGP("Flushing existing user defined chain '%s'\n", chain);
+                                       if (!ip6tc_flush_entries(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error flushing chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               } else {
+                                       DEBUGP("Creating new chain '%s'\n", chain);
+                                       if (!ip6tc_create_chain(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error creating chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               }
                        }
 
                        policy = strtok(NULL, " \t\n");
@@ -321,7 +347,11 @@ int main(int argc, char *argv[])
                        
                        for (curchar = parsestart; *curchar; curchar++) {
                                if (*curchar == '"') {
-                                       if (quote_open) {
+                                       /* quote_open cannot be true if there
+                                        * was no previous character.  Thus, 
+                                        * curchar-1 has to be within bounds */
+                                       if (quote_open && 
+                                           *(curchar-1) != '\\') {
                                                quote_open = 0;
                                                *curchar = ' ';
                                        } else {
@@ -382,6 +412,11 @@ int main(int argc, char *argv[])
                        exit(1);
                }
        }
+       if (in_table) {
+               fprintf(stderr, "%s: COMMIT expected at line %u\n",
+                               program_name, line + 1);
+               exit(1);
+       }
 
        return 0;
 }
index 77cc325..37b1f19 100644 (file)
@@ -100,7 +100,7 @@ static int print_match(const struct ip6t_entry_match *e,
                        const struct ip6t_ip6 *ip)
 {
        struct ip6tables_match *match
-               = find_match(e->u.user.name, TRY_LOAD);
+               = find_match(e->u.user.name, TRY_LOAD, NULL);
 
        if (match) {
                printf("-m %s ", e->u.user.name);
@@ -149,7 +149,7 @@ static void print_rule(const struct ip6t_entry *e,
 
        /* print counters */
        if (counters)
-               printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
+               printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
 
        /* print chain name */
        printf("-A %s ", chain);
@@ -277,7 +277,7 @@ static int do_output(const char *tablename)
                                struct ip6t_counters count;
                                printf("%s ",
                                       ip6tc_get_policy(chain, &count, &h));
-                               printf("[%llu:%llu]\n", count.pcnt, count.bcnt);
+                               printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
                        } else {
                                printf("- [0:0]\n");
                        }
@@ -322,6 +322,10 @@ int main(int argc, char *argv[])
        program_name = "ip6tables-save";
        program_version = IPTABLES_VERSION;
 
+       lib_dir = getenv("IP6TABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IP6T_LIB_DIR;
+
 #ifdef NO_SHARED_LIBS
        init_extensions();
 #endif
index 7391ccf..36dde33 100644 (file)
@@ -46,6 +46,10 @@ main(int argc, char *argv[])
        program_name = "ip6tables";
        program_version = IPTABLES_VERSION;
 
+       lib_dir = getenv("IP6TABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IP6T_LIB_DIR;
+
 #ifdef NO_SHARED_LIBS
        init_extensions();
 #endif
diff --git a/ip6tables.8.in b/ip6tables.8.in
new file mode 100644 (file)
index 0000000..6d3f56c
--- /dev/null
@@ -0,0 +1,461 @@
+.TH IP6TABLES 8 "Mar 09, 2002" "" ""
+.\"
+.\" 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 \- IPv6 packet filter administration
+.SH SYNOPSIS
+.BR "ip6tables [-t table] -[AD] " "chain rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -I " "chain [rulenum] rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -R " "chain rulenum rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -D " "chain rulenum [options]"
+.br
+.BR "ip6tables [-t table] -[LFZ] " "[chain] [options]"
+.br
+.BR "ip6tables [-t table] -N " "chain"
+.br
+.BR "ip6tables [-t table] -X " "[chain]"
+.br
+.BR "ip6tables [-t table] -P " "chain target [options]"
+.br
+.BR "ip6tables [-t table] -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Ip6tables
+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.
+
+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 
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT 
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace (if supported by the kernel).
+.I RETURN
+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
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are currently two independent tables (which tables are present
+at any time depends on the kernel configuration options and which
+modules are present), as nat table has not been implemented yet.
+.TP
+.BI "-t, --table " "table"
+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
+.BR "filter" :
+This is the default table (if no -t option is passed).  It contains
+the built-in chains
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for packets being routed through the box), and
+.B OUTPUT
+(for locally-generated packets).
+.TP
+.BR "mangle" :
+This table is used for specialized packet alteration.  Until kernel
+2.4.17 it had two built-in chains:
+.B PREROUTING
+(for altering incoming packets before routing) and
+.B OUTPUT
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for altering packets being routed through the box), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.RE
+.SH OPTIONS
+The options that are recognized by
+.B ip6tables
+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
+.B ip6tables
+can differentiate it from all other options.
+.TP
+.BI "-A, --append " "chain rule-specification"
+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
+.BI "-D, --delete " "chain rule-specification"
+.ns
+.TP
+.BI "-D, --delete " "chain rulenum"
+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
+.B "-I, --insert"
+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
+.BI "-R, --replace " "chain rulenum rule-specification"
+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
+.BR "-L, --list " "[\fIchain\fP]"
+List all rules in the selected chain.  If no chain is selected, all
+chains are listed.  As every other iptables command, it applies to the
+specified table (filter is the default), so mangle rules get listed by
+.nf
+ ip6tables -t mangle -n -L
+.fi
+Please note that it is often used with the
+.B -n
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the
+.B -Z
+(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
+.BR "-F, --flush " "[\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
+.BR "-Z, --zero " "[\fIchain\fP]"
+Zero the packet and byte counters in all chains.  It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+.BI "-N, --new-chain " "chain"
+Create a new user-defined chain by the given name.  There must be no
+target of that name already.
+.TP
+.BR "-X, --delete-chain " "[\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.  If no argument is given, it
+will attempt to delete every non-builtin chain in the table.
+.TP
+.BI "-P, --policy " "chain target"
+Set the policy for the chain to the given target.  See the section
+.B TARGETS
+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
+.BI "-E, --rename-chain " "old-chain new-chain"
+Rename the user specified chain to the user supplied name.  This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+.B -h
+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
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR ipv6-icmp|icmpv6 ,
+or
+.IR all ,
+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
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a hostname (please note that specifying
+any name to be resolved with a remote query such as DNS is a really bad idea),
+a network IPv6 address (with /mask), or a plain IPv6 address.
+(the network name isn't supported now).
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 64
+is equivalent to
+.IR ffff:ffff:ffff:ffff:0000:0000:0000:0000 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification. 
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax.  The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+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
+.B EXTENSIONS
+below).  If this
+option is omitted in a rule, then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BR "-i, --in-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be received (only for
+packets entering the 
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+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
+.BR "-o, --out-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD 
+and
+.B OUTPUT
+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
+.\" Currently not supported (header-based)
+.\" 
+.\" .B "[!] " "-f, --fragment"
+.\" 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
+.B "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+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
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+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
+.B "-x, --exact"
+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 
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+.B "--modprobe=command"
+When adding or inserting rules into a chain, use
+.B command
+to load any necessary modules (targets, match extensions, etc).
+.SH MATCH EXTENSIONS
+ip6tables can use extended packet matching modules.  These are loaded
+in two ways: implicitly, when
+.B -p
+or
+.B --protocol
+is specified, or with the
+.B -m
+or
+.B --match
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module.  You can specify multiple extended match modules in one line,
+and you can use the
+.B -h
+or
+.B --help
+options after the module has been specified to receive help specific
+to that module.
+
+The following are included in the base package, and most of these can
+be preceded by a
+.B !
+to invert the sense of the match.
+.\" @MATCH@
+.SH TARGET EXTENSIONS
+ip6tables can use extended target modules: the following are included
+in the standard distribution.
+.\" @TARGET@
+.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 
+.B ip6tables
+is very similar to ipchains by Rusty Russell.  The main difference is
+that the chains 
+.B INPUT
+and
+.B OUTPUT
+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 
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.\" .PP The various forms of NAT have been separated out; 
+.\" .B iptables 
+.\" is a pure packet filter when using the default `filter' table, with
+.\" optional extension modules.  This should simplify much of the previous
+.\" confusion over the combination of IP masquerading and packet filtering
+.\" seen previously.  So the following options are handled differently:
+.\" .br
+.\" -j MASQ
+.\" .br
+.\" -M -S
+.\" .br
+.\" -M -L
+.\" .br
+There are several other changes in ip6tables.
+.SH SEE ALSO
+.BR ip6tables-save (8),
+.BR ip6tables-restore(8),
+.BR iptables (8),
+.BR iptables-save (8),
+.BR iptables-restore (8).
+.P
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+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 target, TTL match+target and libipulog.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Jozsef Kadlecsik,
+James Morris, 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 ..
index 553e924..9bfe1d3 100644 (file)
@@ -1,4 +1,4 @@
-/* Code to take an iptables-style command line and do it. */
+/* Code to take an ip6tables-style command line and do it. */
 
 /*
  * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
 #define FALSE 0
 #endif
 
-#ifndef IP6T_LIB_DIR
-#define IP6T_LIB_DIR "/usr/lib/iptables"
-#endif
-
 #ifndef PROC_SYS_MODPROBE
 #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
 #endif
@@ -107,7 +103,7 @@ static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
 #define OPT_COUNTERS   0x00400U
 #define NUMBER_OF_OPT  11
 static const char optflags[NUMBER_OF_OPT]
-= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '3', 'c'};
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c'};
 
 static struct option original_opts[] = {
        { "append", 1, 0, 'A' },
@@ -148,14 +144,6 @@ static struct option original_opts[] = {
  * magic number of -1 */
 int line = -1;
 
-#ifndef __OPTIMIZE__
-struct ip6t_entry_target *
-ip6t_get_target(struct ip6t_entry *e)
-{
-       return (void *)e + e->target_offset;
-}
-#endif
-
 static struct option *opts = original_opts;
 static unsigned int global_option_offset = 0;
 
@@ -203,6 +191,7 @@ static int inverse_for_options[NUMBER_OF_OPT] =
 
 const char *program_version;
 const char *program_name;
+char *lib_dir;
 
 /* Keeping track of external matches and targets: linked lists.  */
 struct ip6tables_match *ip6tables_matches = NULL;
@@ -263,6 +252,16 @@ in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
        /* dst->s6_addr = src->s6_addr; */
 }
 
+static void free_opts(int reset_offset)
+{
+       if (opts != original_opts) {
+               free(opts);
+               opts = original_opts;
+               if (reset_offset)
+                       global_option_offset = 0;
+       }
+}
+
 void
 exit_error(enum exittype status, char *msg, ...)
 {
@@ -277,7 +276,9 @@ exit_error(enum exittype status, char *msg, ...)
                exit_tryhelp(status);
        if (status == VERSION_PROBLEM)
                fprintf(stderr,
-                       "Perhaps iptables or your kernel needs to be upgraded.\n");
+                       "Perhaps ip6tables or your kernel needs to be upgraded.\n");
+       /* On error paths, make sure that we don't leak memory */
+       free_opts(1);
        exit(status);
 }
 
@@ -288,13 +289,14 @@ exit_tryhelp(int status)
                fprintf(stderr, "Error occurred at line: %d\n", line);
        fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
                        program_name, program_name );
+       free_opts(1);
        exit(status);
 }
 
 void
-exit_printhelp(void)
+exit_printhelp(struct ip6tables_rule_match *matches)
 {
-       struct ip6tables_match *m = NULL;
+       struct ip6tables_rule_match *matchp = NULL;
        struct ip6tables_target *t = NULL;
 
        printf("%s v%s\n\n"
@@ -359,14 +361,16 @@ exit_printhelp(void)
 
        /* 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=ip6tables_targets;t;t=t->next) {
-               printf("\n");
-               t->help();
+          specified matches & targets */
+       for (t = ip6tables_targets; t; t = t->next) {
+               if (t->used) {
+                       printf("\n");
+                       t->help();
+               }
        }
-       for (m=ip6tables_matches;m;m=m->next) {
+       for (matchp = matches; matchp; matchp = matchp->next) {
                printf("\n");
-               m->help();
+               matchp->match->help();
        }
        exit(0);
 }
@@ -675,6 +679,7 @@ parse_hostnetworkmask(const char *name, struct in6_addr **addrpp,
        int i, j, n;
 
        strncpy(buf, name, sizeof(buf) - 1);
+       buf[sizeof(buf) - 1] = '\0';
        if ((p = strrchr(buf, '/')) != NULL) {
                *p = '\0';
                addrp = parse_mask(p + 1);
@@ -704,7 +709,7 @@ parse_hostnetworkmask(const char *name, struct in6_addr **addrpp,
 }
 
 struct ip6tables_match *
-find_match(const char *name, enum ip6t_tryload tryload)
+find_match(const char *name, enum ip6t_tryload tryload, struct ip6tables_rule_match **matches)
 {
        struct ip6tables_match *ptr;
        int icmphack = 0;
@@ -729,16 +734,16 @@ find_match(const char *name, enum ip6t_tryload tryload)
 
 #ifndef NO_SHARED_LIBS
        if (!ptr && tryload != DONT_LOAD) {
-               char path[sizeof(IP6T_LIB_DIR) + sizeof("/libip6t_.so")
+               char path[strlen(lib_dir) + sizeof("/libip6t_.so")
                         + strlen(name)];
                if (!icmphack)
-                       sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", name);
+                       sprintf(path, "%s/libip6t_%s.so", lib_dir, name);
                else
-                       sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", "icmpv6");
+                       sprintf(path, "%s/libip6t_%s.so", lib_dir, "icmpv6");
                if (dlopen(path, RTLD_NOW)) {
                        /* Found library.  If it didn't register itself,
                           maybe they specified target as match. */
-                       ptr = find_match(name, DONT_LOAD);
+                       ptr = find_match(name, DONT_LOAD, NULL);
 
                        if (!ptr)
                                exit_error(PARAMETER_PROBLEM,
@@ -762,15 +767,24 @@ find_match(const char *name, enum ip6t_tryload tryload)
        }
 #endif
 
-       if (ptr)
-               ptr->used = 1;
+       if (ptr && matches) {
+               struct ip6tables_rule_match **i;
+               struct ip6tables_rule_match *newentry;
+
+               newentry = fw_malloc(sizeof(struct ip6tables_rule_match));
+
+               for (i = matches; *i; i = &(*i)->next);
+               newentry->match = ptr;
+               newentry->next = NULL;
+               *i = newentry;
+       }
 
        return ptr;
 }
 
 /* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
 static struct ip6tables_match *
-find_proto(const char *pname, enum ip6t_tryload tryload, int nolookup)
+find_proto(const char *pname, enum ip6t_tryload tryload, int nolookup, struct ip6tables_rule_match **matches)
 {
        unsigned int proto;
 
@@ -778,9 +792,9 @@ find_proto(const char *pname, enum ip6t_tryload tryload, int nolookup)
                char *protoname = proto_to_name(proto, nolookup);
 
                if (protoname)
-                       return find_match(protoname, tryload);
+                       return find_match(protoname, tryload, matches);
        } else
-               return find_match(pname, tryload);
+               return find_match(pname, tryload, matches);
 
        return NULL;
 }
@@ -815,8 +829,7 @@ parse_protocol(const char *s)
        return (u_int16_t)proto;
 }
 
-static void
-parse_interface(const char *arg, char *vianame, unsigned char *mask)
+void parse_interface(const char *arg, char *vianame, unsigned char *mask)
 {
        int vialen = strlen(arg);
        unsigned int i;
@@ -830,7 +843,7 @@ parse_interface(const char *arg, char *vianame, unsigned char *mask)
                           " (%i)", arg, IFNAMSIZ-1);
 
        strcpy(vianame, arg);
-       if (vialen == 0)
+       if ((vialen == 0) || (vialen == 1 && vianame[0] == '+'))
                memset(mask, 0, IFNAMSIZ);
        else if (vianame[vialen - 1] == '+') {
                memset(mask, 0xFF, vialen - 1);
@@ -877,8 +890,8 @@ parse_target(const char *targetname)
 
        if (strlen(targetname)+1 > sizeof(ip6t_chainlabel))
                exit_error(PARAMETER_PROBLEM,
-                          "Invalid target name `%s' (%i chars max)",
-                          targetname, sizeof(ip6t_chainlabel)-1);
+                          "Invalid target name `%s' (%u chars max)",
+                          targetname, (unsigned int)sizeof(ip6t_chainlabel)-1);
 
        for (ptr = targetname; *ptr; ptr++)
                if (isspace(*ptr))
@@ -888,18 +901,18 @@ parse_target(const char *targetname)
 }
 
 int
-string_to_number(const char *s, unsigned int min, unsigned int max,
-                unsigned int *ret)
+string_to_number_ll(const char *s, unsigned long long min, unsigned long long max,
+                unsigned long long *ret)
 {
-       long number;
+       unsigned long long number;
        char *end;
 
        /* Handle hex, octal, etc. */
        errno = 0;
-       number = strtol(s, &end, 0);
+       number = strtoull(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) {
+               if (errno != ERANGE && min <= number && (!max || number <= max)) {
                        *ret = number;
                        return 0;
                }
@@ -907,6 +920,31 @@ string_to_number(const char *s, unsigned int min, unsigned int max,
        return -1;
 }
 
+int
+string_to_number_l(const char *s, unsigned long min, unsigned long max,
+                unsigned long *ret)
+{
+       int result;
+       unsigned long long number;
+
+       result = string_to_number_ll(s, min, max, &number);
+       *ret = (unsigned long)number;
+
+       return result;
+}
+
+int string_to_number(const char *s, unsigned int min, unsigned int max,
+               unsigned int *ret)
+{
+       int result;
+       unsigned long number;
+
+       result = string_to_number_l(s, min, max, &number);
+       *ret = (unsigned int)number;
+
+       return result;
+}
+
 static void
 set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
           int invert)
@@ -948,9 +986,9 @@ find_target(const char *name, enum ip6t_tryload tryload)
 
 #ifndef NO_SHARED_LIBS
        if (!ptr && tryload != DONT_LOAD) {
-               char path[sizeof(IP6T_LIB_DIR) + sizeof("/libip6t_.so")
+               char path[strlen(lib_dir) + sizeof("/libip6t_.so")
                         + strlen(name)];
-               sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", name);
+               sprintf(path, "%s/libip6t_%s.so", lib_dir, name);
                if (dlopen(path, RTLD_NOW)) {
                        /* Found library.  If it didn't register itself,
                           maybe they specified match as a target. */
@@ -990,6 +1028,9 @@ merge_options(struct option *oldopts, const struct option *newopts,
        unsigned int num_old, num_new, i;
        struct option *merge;
 
+       /* Release previous options merged if any */
+       free_opts(0);
+
        for (num_old = 0; oldopts[num_old].name; num_old++);
        for (num_new = 0; newopts[num_new].name; num_new++);
 
@@ -1018,7 +1059,7 @@ register_match6(struct ip6tables_match *me)
                exit(1);
        }
 
-       if (find_match(me->name, DONT_LOAD)) {
+       if (find_match(me->name, DONT_LOAD, NULL)) {
                fprintf(stderr, "%s: match `%s' already registered.\n",
                        program_name, me->name);
                exit(1);
@@ -1026,7 +1067,7 @@ register_match6(struct ip6tables_match *me)
 
        if (me->size != IP6T_ALIGN(me->size)) {
                fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
-                       program_name, me->name, me->size);
+                       program_name, me->name, (unsigned int)me->size);
                exit(1);
        }
 
@@ -1056,7 +1097,7 @@ register_target6(struct ip6tables_target *me)
 
        if (me->size != IP6T_ALIGN(me->size)) {
                fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
-                       program_name, me->name, me->size);
+                       program_name, me->name, (unsigned int)me->size);
                exit(1);
        }
 
@@ -1079,17 +1120,17 @@ print_num(u_int64_t number, unsigned int format)
                                        number = (number + 500) / 1000;
                                        if (number > 9999) {
                                                number = (number + 500) / 1000;
-                                               printf(FMT("%4lluT ","%lluT "), number);
+                                               printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
                                        }
-                                       else printf(FMT("%4lluG ","%lluG "), number);
+                                       else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
                                }
-                               else printf(FMT("%4lluM ","%lluM "), number);
+                               else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
                        } else
-                               printf(FMT("%4lluK ","%lluK "), number);
+                               printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
                } else
-                       printf(FMT("%5llu ","%llu "), number);
+                       printf(FMT("%5llu ","%llu "), (unsigned long long)number);
        } else
-               printf(FMT("%8llu ","%llu "), number);
+               printf(FMT("%8llu ","%llu "), (unsigned long long)number);
 }
 
 
@@ -1148,7 +1189,7 @@ print_match(const struct ip6t_entry_match *m,
            const struct ip6t_ip6 *ip,
            int numeric)
 {
-       struct ip6tables_match *match = find_match(m->u.user.name, TRY_LOAD);
+       struct ip6tables_match *match = find_match(m->u.user.name, TRY_LOAD, NULL);
 
        if (match) {
                if (match->print)
@@ -1279,7 +1320,7 @@ print_firewall(const struct ip6t_entry *fw,
                        target->print(&fw->ipv6, t, format & FMT_NUMERIC);
        } else if (t->u.target_size != sizeof(*t))
                printf("[%u bytes of unknown target data] ",
-                      t->u.target_size - sizeof(*t));
+                      (unsigned int)(t->u.target_size - sizeof(*t)));
 
        if (!(format & FMT_NONEWLINE))
                fputc('\n', stdout);
@@ -1366,20 +1407,16 @@ insert_entry(const ip6t_chainlabel chain,
 }
 
 static unsigned char *
-make_delete_mask(struct ip6t_entry *fw)
+make_delete_mask(struct ip6t_entry *fw, struct ip6tables_rule_match *matches)
 {
        /* Establish mask for comparison */
        unsigned int size;
-       struct ip6tables_match *m;
+       struct ip6tables_rule_match *matchp;
        unsigned char *mask, *mptr;
 
        size = sizeof(struct ip6t_entry);
-       for (m = ip6tables_matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
-               size += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + m->size;
-       }
+       for (matchp = matches; matchp; matchp = matchp->next)
+               size += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + matchp->match->size;
 
        mask = fw_calloc(1, size
                         + IP6T_ALIGN(sizeof(struct ip6t_entry_target))
@@ -1388,14 +1425,11 @@ make_delete_mask(struct ip6t_entry *fw)
        memset(mask, 0xFF, sizeof(struct ip6t_entry));
        mptr = mask + sizeof(struct ip6t_entry);
 
-       for (m = ip6tables_matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
+       for (matchp = matches; matchp; matchp = matchp->next) {
                memset(mptr, 0xFF,
                       IP6T_ALIGN(sizeof(struct ip6t_entry_match))
-                      + m->userspacesize);
-               mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + m->size;
+                      + matchp->match->userspacesize);
+               mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + matchp->match->size;
        }
 
        memset(mptr, 0xFF, 
@@ -1413,13 +1447,14 @@ delete_entry(const ip6t_chainlabel chain,
             unsigned int ndaddrs,
             const struct in6_addr daddrs[],
             int verbose,
-            ip6tc_handle_t *handle)
+            ip6tc_handle_t *handle,
+            struct ip6tables_rule_match *matches)
 {
        unsigned int i, j;
        int ret = 1;
        unsigned char *mask;
 
-       mask = make_delete_mask(fw);
+       mask = make_delete_mask(fw, matches);
        for (i = 0; i < nsaddrs; i++) {
                fw->ipv6.src = saddrs[i];
                for (j = 0; j < ndaddrs; j++) {
@@ -1429,6 +1464,8 @@ delete_entry(const ip6t_chainlabel chain,
                        ret &= ip6tc_delete_entry(chain, fw, mask, handle);
                }
        }
+       free(mask);
+
        return ret;
 }
 
@@ -1459,7 +1496,7 @@ for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *),
        for (i = 0; i < chaincount; i++) {
                if (!builtinstoo
                    && ip6tc_builtin(chains + i*sizeof(ip6t_chainlabel),
-                                   *handle))
+                                   *handle) == 1)
                        continue;
                ret &= fn(chains + i*sizeof(ip6t_chainlabel), verbose, handle);
        }
@@ -1562,15 +1599,17 @@ static char *get_modprobe(void)
        int procfile;
        char *ret;
 
+#define PROCFILE_BUFSIZ 1024
        procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
        if (procfile < 0)
                return NULL;
 
-       ret = malloc(1024);
+       ret = malloc(PROCFILE_BUFSIZ);
        if (ret) {
-               switch (read(procfile, ret, 1024)) {
+               memset(ret, 0, PROCFILE_BUFSIZ);
+               switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
                case -1: goto fail;
-               case 1024: goto fail; /* Partial read.  Wierd */
+               case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
                }
                if (ret[strlen(ret)-1]=='\n') 
                        ret[strlen(ret)-1]=0;
@@ -1587,7 +1626,7 @@ int ip6tables_insmod(const char *modname, const char *modprobe)
 {
        char *buf = NULL;
        char *argv[3];
-       int i=0;
+       int status;
 
        /* If they don't explicitly set it, read out of kernel */
        if (!modprobe) {
@@ -1599,44 +1638,38 @@ int ip6tables_insmod(const char *modname, const char *modprobe)
 
        switch (fork()) {
        case 0:
-               /* close open file descriptors */
-               for (i=0; i< 10; i++) {
-                 close(i);
-               }
                argv[0] = (char *)modprobe;
                argv[1] = (char *)modname;
                argv[2] = NULL;
                execv(argv[0], argv);
 
                /* not usually reached */
-               exit(0);
+               exit(1);
        case -1:
                return -1;
 
        default: /* parent */
-               wait(NULL);
+               wait(&status);
        }
 
        free(buf);
-       return 0;
+       if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+               return 0;
+       return -1;
 }
 
 static struct ip6t_entry *
 generate_entry(const struct ip6t_entry *fw,
-              struct ip6tables_match *matches,
+              struct ip6tables_rule_match *matches,
               struct ip6t_entry_target *target)
 {
        unsigned int size;
-       struct ip6tables_match *m;
+       struct ip6tables_rule_match *matchp;
        struct ip6t_entry *e;
 
        size = sizeof(struct ip6t_entry);
-       for (m = matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
-               size += m->m->u.match_size;
-       }
+       for (matchp = matches; matchp; matchp = matchp->next)
+               size += matchp->match->m->u.match_size;
 
        e = fw_malloc(size + target->u.target_size);
        *e = *fw;
@@ -1644,18 +1677,30 @@ generate_entry(const struct ip6t_entry *fw,
        e->next_offset = size + target->u.target_size;
 
        size = 0;
-       for (m = matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
-               memcpy(e->elems + size, m->m, m->m->u.match_size);
-               size += m->m->u.match_size;
+       for (matchp = matches; matchp; matchp = matchp->next) {
+               memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
+               size += matchp->match->m->u.match_size;
        }
        memcpy(e->elems + size, target, target->u.target_size);
 
        return e;
 }
 
+void clear_rule_matches(struct ip6tables_rule_match **matches)
+{
+       struct ip6tables_rule_match *matchp, *tmp;
+
+       for (matchp = *matches; matchp;) {
+               tmp = matchp->next;
+               if (matchp->match->m)
+                       free(matchp->match->m);
+               free(matchp);
+               matchp = tmp;
+       }
+
+       *matches = NULL;
+}
+
 int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
 {
        struct ip6t_entry fw, *e = NULL;
@@ -1671,6 +1716,8 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
        const char *pcnt = NULL, *bcnt = NULL;
        int ret = 1;
        struct ip6tables_match *m;
+       struct ip6tables_rule_match *matches = NULL;
+       struct ip6tables_rule_match *matchp;
        struct ip6tables_target *target = NULL;
        struct ip6tables_target *t;
        const char *jumpto = "";
@@ -1681,19 +1728,14 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
 
        memset(&fw, 0, sizeof(fw));
 
-       opts = original_opts;
-       global_option_offset = 0;
-
        /* re-set optind to 0 in case do_command gets called
         * a second time */
        optind = 0;
 
        /* clear mflags in case do_command gets called a second time
         * (we clear the global list of all matches for security)*/
-       for (m = ip6tables_matches; m; m = m->next) {
+       for (m = ip6tables_matches; m; m = m->next)
                m->mflags = 0;
-               m->used = 0;
-       }
 
        for (t = ip6tables_targets; t; t = t->next) {
                t->tflags = 0;
@@ -1779,10 +1821,10 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        break;
 
                case 'N':
-                       if (optarg && *optarg == '-')
+                       if (optarg && (*optarg == '-' || *optarg == '!'))
                                exit_error(PARAMETER_PROBLEM,
                                           "chain name not allowed to start "
-                                          "with `-'\n");
+                                          "with `%c'\n", *optarg);
                        if (find_target(optarg, TRY_LOAD))
                                exit_error(PARAMETER_PROBLEM,
                                           "chain name may not clash "
@@ -1832,11 +1874,11 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        if (!optarg)
                                optarg = argv[optind];
 
-                       /* iptables -p icmp -h */
-                       if (!ip6tables_matches && protocol)
-                               find_match(protocol, TRY_LOAD);
+                       /* ip6tables -p icmp -h */
+                       if (!matches && protocol)
+                               find_match(protocol, TRY_LOAD, &matches);
 
-                       exit_printhelp();
+                       exit_printhelp(matches);
 
                        /*
                         * Option selection
@@ -1860,7 +1902,6 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                            && (fw.ipv6.invflags & IP6T_INV_PROTO))
                                exit_error(PARAMETER_PROBLEM,
                                           "rule would never match protocol");
-                       fw.nfcache |= NFC_IP6_PROTO;
                        break;
 
                case 's':
@@ -1868,7 +1909,6 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        set_option(&options, OPT_SOURCE, &fw.ipv6.invflags,
                                   invert);
                        shostnetworkmask = argv[optind-1];
-                       fw.nfcache |= NFC_IP6_SRC;
                        break;
 
                case 'd':
@@ -1876,7 +1916,6 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        set_option(&options, OPT_DESTINATION, &fw.ipv6.invflags,
                                   invert);
                        dhostnetworkmask = argv[optind-1];
-                       fw.nfcache |= NFC_IP6_DST;
                        break;
 
                case 'j':
@@ -1895,7 +1934,8 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                                target->t = fw_calloc(1, size);
                                target->t->u.target_size = size;
                                strcpy(target->t->u.user.name, jumpto);
-                               target->init(target->t, &fw.nfcache);
+                               if (target->init != NULL)
+                                       target->init(target->t, &fw.nfcache);
                                opts = merge_options(opts, target->extra_opts, &target->option_offset);
                        }
                        break;
@@ -1908,7 +1948,6 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        parse_interface(argv[optind-1],
                                        fw.ipv6.iniface,
                                        fw.ipv6.iniface_mask);
-                       fw.nfcache |= NFC_IP6_IF_IN;
                        break;
 
                case 'o':
@@ -1918,7 +1957,6 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        parse_interface(argv[optind-1],
                                        fw.ipv6.outiface,
                                        fw.ipv6.outiface_mask);
-                       fw.nfcache |= NFC_IP6_IF_OUT;
                        break;
 
                case 'v':
@@ -1935,13 +1973,14 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                                exit_error(PARAMETER_PROBLEM,
                                           "unexpected ! flag before --match");
 
-                       m = find_match(optarg, LOAD_MUST_SUCCEED);
+                       m = find_match(optarg, LOAD_MUST_SUCCEED, &matches);
                        size = IP6T_ALIGN(sizeof(struct ip6t_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);
+                       if (m->init != NULL)
+                               m->init(m->m, &fw.nfcache);
                        opts = merge_options(opts, m->extra_opts, &m->option_offset);
                }
                break;
@@ -1993,12 +2032,12 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                                        "-%c requires packet and byte counter",
                                        opt2char(OPT_COUNTERS));
 
-                       if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+                       if (sscanf(pcnt, "%llu", (unsigned long long *)&fw.counters.pcnt) != 1)
                                exit_error(PARAMETER_PROBLEM,
                                        "-%c packet counter not numeric",
                                        opt2char(OPT_COUNTERS));
 
-                       if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+                       if (sscanf(bcnt, "%llu", (unsigned long long *)&fw.counters.bcnt) != 1)
                                exit_error(PARAMETER_PROBLEM,
                                        "-%c byte counter not numeric",
                                        opt2char(OPT_COUNTERS));
@@ -2027,18 +2066,16 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                                               argv, invert,
                                               &target->tflags,
                                               &fw, &target->t))) {
-                               for (m = ip6tables_matches; m; m = m->next) {
-                                       if (!m->used)
-                                               continue;
-
-                                       if (m->parse(c - m->option_offset,
+                               for (matchp = matches; matchp; matchp = matchp->next) {
+                                       if (matchp->match->parse(c - matchp->match->option_offset,
                                                     argv, invert,
-                                                    &m->mflags,
+                                                    &matchp->match->mflags,
                                                     &fw,
                                                     &fw.nfcache,
-                                                    &m->m))
+                                                    &matchp->match->m))
                                                break;
                                }
+                               m = matchp ? matchp->match : NULL;
 
                                /* If you listen carefully, you can
                                   actually hear this code suck. */
@@ -2059,20 +2096,20 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                                 * - a protocol has been specified
                                 * - the protocol extension has not been
                                 *   loaded yet, or is loaded and unused
-                                *   [think of iptables-restore!]
+                                *   [think of ip6tables-restore!]
                                 * - the protocol extension can be successively
                                 *   loaded
                                 */
                                if (m == NULL
                                    && protocol
                                    && (!find_proto(protocol, DONT_LOAD,
-                                                  options&OPT_NUMERIC) 
+                                                  options&OPT_NUMERIC, NULL
                                        || (find_proto(protocol, DONT_LOAD,
-                                                       options&OPT_NUMERIC)
+                                                       options&OPT_NUMERIC, NULL)
                                            && (proto_used == 0))
                                       )
                                    && (m = find_proto(protocol, TRY_LOAD,
-                                                      options&OPT_NUMERIC))) {
+                                                      options&OPT_NUMERIC, &matches))) {
                                        /* Try loading protocol */
                                        size_t size;
                                        
@@ -2084,7 +2121,8 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                                        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);
+                                       if (m->init != NULL)
+                                               m->init(m->m, &fw.nfcache);
 
                                        opts = merge_options(opts,
                                            m->extra_opts, &m->option_offset);
@@ -2102,12 +2140,8 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                invert = FALSE;
        }
 
-       for (m = ip6tables_matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
-               m->final_check(m->mflags);
-       }
+       for (matchp = matches; matchp; matchp = matchp->next)
+               matchp->match->final_check(matchp->match->mflags);
 
        if (target)
                target->final_check(target->tflags);
@@ -2158,11 +2192,9 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
        if (!*handle)
                *handle = ip6tc_init(*table);
 
-       if (!*handle) {
-               /* try to insmod the module if iptc_init failed */
-               ip6tables_insmod("ip6_tables", modprobe);
+       /* try to insmod the module if iptc_init failed */
+       if (!*handle && ip6tables_insmod("ip6_tables", modprobe) != -1)
                *handle = ip6tc_init(*table);
-       }
 
        if (!*handle)
                exit_error(VERSION_PROBLEM,
@@ -2197,6 +2229,9 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        printf("Warning: using chain %s, not extension\n",
                               jumpto);
 
+                       if (target->t)
+                               free(target->t);
+
                        target = NULL;
                }
 
@@ -2215,7 +2250,8 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        target->t = fw_calloc(1, size);
                        target->t->u.target_size = size;
                        strcpy(target->t->u.user.name, jumpto);
-                       target->init(target->t, &fw.nfcache);
+                       if (target->init != NULL)
+                               target->init(target->t, &fw.nfcache);
                }
 
                if (!target) {
@@ -2225,7 +2261,8 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                         * chain. */
                        find_target(jumpto, LOAD_MUST_SUCCEED);
                } else {
-                       e = generate_entry(&fw, ip6tables_matches, target->t);
+                       e = generate_entry(&fw, matches, target->t);
+                       free(target->t);
                }
        }
 
@@ -2240,7 +2277,7 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                ret = delete_entry(chain, e,
                                   nsaddrs, saddrs, ndaddrs, daddrs,
                                   options&OPT_VERBOSE,
-                                  handle);
+                                  handle, matches);
                break;
        case CMD_DELETE_NUM:
                ret = ip6tc_delete_num_entry(chain, rulenum - 1, handle);
@@ -2301,5 +2338,20 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
        if (verbose > 1)
                dump_entries6(*handle);
 
+       clear_rule_matches(&matches);
+
+       if (e != NULL) {
+               free(e);
+               e = NULL;
+       }
+
+       for (c = 0; c < nsaddrs; c++)
+               free(&saddrs[c]);
+
+       for (c = 0; c < ndaddrs; c++)
+               free(&daddrs[c]);
+
+       free_opts(1);
+
        return ret;
 }
diff --git a/iptables-multi.c b/iptables-multi.c
new file mode 100644 (file)
index 0000000..0563099
--- /dev/null
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+int iptables_main(int argc, char **argv);
+int iptables_save_main(int argc, char **argv);
+int iptables_restore_main(int argc, char **argv);
+
+int main(int argc, char **argv) {
+  char *progname;
+
+  if (argc == 0) {
+    fprintf(stderr, "no argv[0]?");
+    exit(1);
+  } else {
+    progname = basename(argv[0]);
+
+    if (!strcmp(progname, "iptables"))
+      return iptables_main(argc, argv);
+    
+    if (!strcmp(progname, "iptables-save"))
+      return iptables_save_main(argc, argv);
+    
+    if (!strcmp(progname, "iptables-restore"))
+      return iptables_restore_main(argc, argv);
+    
+    fprintf(stderr, "iptables multi-purpose version: unknown applet name %s\n", progname);
+    exit(1);
+  }
+}
index 79cb259..95e2cbe 100644 (file)
@@ -4,7 +4,7 @@
  *
  * This code is distributed under the terms of GNU GPL v2
  *
- * $Id: iptables-restore.c,v 1.26 2003/05/02 15:30:11 laforge Exp $
+ * $Id: iptables-restore.c 3980 2005-06-12 15:54:15Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kaber/emailAddress=kaber@netfilter.org $
  */
 
 #include <getopt.h>
@@ -27,7 +27,8 @@ static int binary = 0, counters = 0, verbose = 0, noflush = 0;
 static struct option options[] = {
        { "binary", 0, 0, 'b' },
        { "counters", 0, 0, 'c' },
-       { "verbose", 1, 0, 'v' },
+       { "verbose", 0, 0, 'v' },
+       { "test", 0, 0, 't' },
        { "help", 0, 0, 'h' },
        { "noflush", 0, 0, 'n'},
        { "modprobe", 1, 0, 'M'},
@@ -38,10 +39,11 @@ 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] [-h]\n"
+       fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
                        "          [ --binary ]\n"
                        "          [ --counters ]\n"
                        "          [ --verbose ]\n"
+                       "          [ --test ]\n"
                        "          [ --help ]\n"
                        "          [ --noflush ]\n"
                        "          [ --modprobe=<command>]\n", name);
@@ -71,11 +73,7 @@ iptc_handle_t create_handle(const char *tablename, const char* modprobe )
 
 int parse_counters(char *string, struct ipt_counters *ctr)
 {
-       if (string != NULL)
-               return (sscanf(string, "[%llu:%llu]", &ctr->pcnt, &ctr->bcnt)
-                       == 2);
-       else
-               return (0 == 2);
+       return (sscanf(string, "[%llu:%llu]", (unsigned long long *)&ctr->pcnt, (unsigned long long *)&ctr->bcnt) == 2);
 }
 
 /* global new argv and argc */
@@ -101,7 +99,13 @@ static void free_argv(void) {
                free(newargv[i]);
 }
 
-int main(int argc, char *argv[])
+#ifdef IPTABLES_MULTI
+int
+iptables_restore_main(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
 {
        iptc_handle_t handle = NULL;
        char buffer[10240];
@@ -109,17 +113,21 @@ int main(int argc, char *argv[])
        char curtable[IPT_TABLE_MAXNAMELEN + 1];
        FILE *in;
        const char *modprobe = 0;
-       int in_table = 0;
+       int in_table = 0, testing = 0;
 
        program_name = "iptables-restore";
        program_version = IPTABLES_VERSION;
        line = 0;
 
+       lib_dir = getenv("IPTABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IPT_LIB_DIR;
+
 #ifdef NO_SHARED_LIBS
        init_extensions();
 #endif
 
-       while ((c = getopt_long(argc, argv, "bcvhnM:", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) {
                switch (c) {
                        case 'b':
                                binary = 1;
@@ -130,6 +138,9 @@ int main(int argc, char *argv[])
                        case 'v':
                                verbose = 1;
                                break;
+                       case 't':
+                               testing = 1;
+                               break;
                        case 'h':
                                print_usage("iptables-restore",
                                            IPTABLES_VERSION);
@@ -162,13 +173,20 @@ int main(int argc, char *argv[])
                int ret = 0;
 
                line++;
-               if (buffer[0] == '\n') continue;
+               if (buffer[0] == '\n')
+                       continue;
                else if (buffer[0] == '#') {
-                       if (verbose) fputs(buffer, stdout);
+                       if (verbose)
+                               fputs(buffer, stdout);
                        continue;
                } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
-                       DEBUGP("Calling commit\n");
-                       ret = iptc_commit(&handle);
+                       if (!testing) {
+                               DEBUGP("Calling commit\n");
+                               ret = iptc_commit(&handle);
+                       } else {
+                               DEBUGP("Not calling commit, testing\n");
+                               ret = 1;
+                       }
                        in_table = 0;
                } else if ((buffer[0] == '*') && (!in_table)) {
                        /* New table */
@@ -183,6 +201,7 @@ int main(int argc, char *argv[])
                                exit(1);
                        }
                        strncpy(curtable, table, IPT_TABLE_MAXNAMELEN);
+                       curtable[IPT_TABLE_MAXNAMELEN] = '\0';
 
                        if (handle)
                                iptc_free(&handle);
@@ -216,13 +235,22 @@ int main(int argc, char *argv[])
                                exit(1);
                        }
 
-                       if (!iptc_builtin(chain, handle)) {
-                               DEBUGP("Creating new chain '%s'\n", chain);
-                               if (!iptc_create_chain(chain, &handle)) 
-                                       exit_error(PARAMETER_PROBLEM, 
-                                                  "error creating chain "
-                                                  "'%s':%s\n", chain, 
-                                                  strerror(errno));
+                       if (iptc_builtin(chain, handle) <= 0) {
+                               if (noflush && iptc_is_chain(chain, handle)) {
+                                       DEBUGP("Flushing existing user defined chain '%s'\n", chain);
+                                       if (!iptc_flush_entries(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error flushing chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               } else {
+                                       DEBUGP("Creating new chain '%s'\n", chain);
+                                       if (!iptc_create_chain(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error creating chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               }
                        }
 
                        policy = strtok(NULL, " \t\n");
@@ -322,7 +350,11 @@ int main(int argc, char *argv[])
                        
                        for (curchar = parsestart; *curchar; curchar++) {
                                if (*curchar == '"') {
-                                       if (quote_open) {
+                                       /* quote_open cannot be true if there
+                                        * was no previous character.  Thus, 
+                                        * curchar-1 has to be within bounds */
+                                       if (quote_open && 
+                                           *(curchar-1) != '\\') {
                                                quote_open = 0;
                                                *curchar = ' ';
                                        } else {
@@ -383,6 +415,11 @@ int main(int argc, char *argv[])
                        exit(1);
                }
        }
+       if (in_table) {
+               fprintf(stderr, "%s: COMMIT expected at line %u\n",
+                               program_name, line + 1);
+               exit(1);
+       }
 
        return 0;
 }
index 90163b5..6c7267e 100644 (file)
@@ -13,6 +13,7 @@
 #include <string.h>
 #include <dlfcn.h>
 #include <time.h>
+#include <netdb.h>
 #include "libiptc/libiptc.h"
 #include "iptables.h"
 
@@ -75,6 +76,7 @@ static const struct pprot chain_protos[] = {
        { "icmp", IPPROTO_ICMP },
        { "esp", IPPROTO_ESP },
        { "ah", IPPROTO_AH },
+       { "sctp", IPPROTO_SCTP },
 };
 
 static void print_proto(u_int16_t proto, int invert)
@@ -83,6 +85,12 @@ static void print_proto(u_int16_t proto, int invert)
                unsigned int i;
                const char *invertstr = invert ? "! " : "";
 
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent) {
+                       printf("-p %s%s ", invertstr, pent->p_name);
+                       return;
+               }
+
                for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
                        if (chain_protos[i].num == proto) {
                                printf("-p %s%s ",
@@ -111,7 +119,7 @@ static int print_match(const struct ipt_entry_match *e,
                        const struct ipt_ip *ip)
 {
        struct iptables_match *match
-               = find_match(e->u.user.name, TRY_LOAD);
+               = find_match(e->u.user.name, TRY_LOAD, NULL);
 
        if (match) {
                printf("-m %s ", e->u.user.name);
@@ -157,7 +165,7 @@ static void print_rule(const struct ipt_entry *e,
 
        /* print counters */
        if (counters)
-               printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
+               printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
 
        /* print chain name */
        printf("-A %s ", chain);
@@ -276,7 +284,7 @@ static int do_output(const char *tablename)
                                struct ipt_counters count;
                                printf("%s ",
                                       iptc_get_policy(chain, &count, &h));
-                               printf("[%llu:%llu]\n", count.pcnt, count.bcnt);
+                               printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
                        } else {
                                printf("- [0:0]\n");
                        }
@@ -313,7 +321,13 @@ static int do_output(const char *tablename)
  * :Chain name POLICY packets bytes
  * rule
  */
-int main(int argc, char *argv[])
+#ifdef IPTABLES_MULTI
+int
+iptables_save_main(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
 {
        const char *tablename = NULL;
        int c;
@@ -321,6 +335,10 @@ int main(int argc, char *argv[])
        program_name = "iptables-save";
        program_version = IPTABLES_VERSION;
 
+       lib_dir = getenv("IPTABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IPT_LIB_DIR;
+
 #ifdef NO_SHARED_LIBS
        init_extensions();
 #endif
index 8a4c90e..257b677 100644 (file)
 #include <string.h>
 #include <iptables.h>
 
+#ifdef IPTABLES_MULTI
+int
+iptables_main(int argc, char *argv[])
+#else
 int
 main(int argc, char *argv[])
+#endif
 {
        int ret;
        char *table = "filter";
@@ -47,6 +52,10 @@ main(int argc, char *argv[])
        program_name = "iptables";
        program_version = IPTABLES_VERSION;
 
+       lib_dir = getenv("IPTABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IPT_LIB_DIR;
+
 #ifdef NO_SHARED_LIBS
        init_extensions();
 #endif
diff --git a/iptables.8.in b/iptables.8.in
new file mode 100644 (file)
index 0000000..0d17bd5
--- /dev/null
@@ -0,0 +1,474 @@
+.TH IPTABLES 8 "Mar 09, 2002" "" ""
+.\"
+.\" Man page written by Herve Eychenne <rv@wallfire.org> (May 1999)
+.\" It is based on ipchains page.
+.\" TODO: add a word for protocol helpers (FTP, IRC, SNMP-ALG)
+.\"
+.\" 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
+iptables \- administration tool for IPv4 packet filtering and NAT
+.SH SYNOPSIS
+.BR "iptables [-t table] -[AD] " "chain rule-specification [options]"
+.br
+.BR "iptables [-t table] -I " "chain [rulenum] rule-specification [options]"
+.br
+.BR "iptables [-t table] -R " "chain rulenum rule-specification [options]"
+.br
+.BR "iptables [-t table] -D " "chain rulenum [options]"
+.br
+.BR "iptables [-t table] -[LFZ] " "[chain] [options]"
+.br
+.BR "iptables [-t table] -N " "chain"
+.br
+.BR "iptables [-t table] -X " "[chain]"
+.br
+.BR "iptables [-t table] -P " "chain target [options]"
+.br
+.BR "iptables [-t table] -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Iptables
+is used to set up, maintain, and inspect the tables of IP 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.
+
+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 
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT 
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace (if supported by the kernel).
+.I RETURN
+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
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are currently three independent tables (which tables are present
+at any time depends on the kernel configuration options and which
+modules are present).
+.TP
+.BI "-t, --table " "table"
+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
+.BR "filter" :
+This is the default table (if no -t option is passed).  It contains
+the built-in chains
+.B INPUT
+(for packets destined to local sockets),
+.B FORWARD
+(for packets being routed through the box), and
+.B OUTPUT
+(for locally-generated packets).
+.TP
+.BR "nat" :
+This table is consulted when a packet that creates a new
+connection is encountered.  It consists of three built-ins:
+.B PREROUTING
+(for altering packets as soon as they come in),
+.B OUTPUT
+(for altering locally-generated packets before routing), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "mangle" :
+This table is used for specialized packet alteration.  Until kernel
+2.4.17 it had two built-in chains:
+.B PREROUTING
+(for altering incoming packets before routing) and
+.B OUTPUT
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for altering packets being routed through the box), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "raw" :
+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:
+.B PREROUTING
+(for packets arriving via any network interface)
+.B OUTPUT
+(for packets generated by local processes)
+.RE
+.SH OPTIONS
+The options that are recognized by
+.B iptables
+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
+.B iptables
+can differentiate it from all other options.
+.TP
+.BI "-A, --append " "chain rule-specification"
+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
+.BI "-D, --delete " "chain rule-specification"
+.ns
+.TP
+.BI "-D, --delete " "chain rulenum"
+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
+.BR "-I, --insert " "\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
+.BI "-R, --replace " "chain rulenum rule-specification"
+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
+.BR "-L, --list " "[\fIchain\fP]"
+List all rules in the selected chain.  If no chain is selected, all
+chains are listed.  As every other iptables command, it applies to the
+specified table (filter is the default), so NAT rules get listed by
+.nf
+ iptables -t nat -n -L
+.fi
+Please note that it is often used with the
+.B -n
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the
+.B -Z
+(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
+ iptables -L -v
+.fi
+.TP
+.BR "-F, --flush " "[\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
+.BR "-Z, --zero " "[\fIchain\fP]"
+Zero the packet and byte counters in all chains.  It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+.BI "-N, --new-chain " "chain"
+Create a new user-defined chain by the given name.  There must be no
+target of that name already.
+.TP
+.BR "-X, --delete-chain " "[\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.  If no argument is given, it
+will attempt to delete every non-builtin chain in the table.
+.TP
+.BI "-P, --policy " "chain target"
+Set the policy for the chain to the given target.  See the section
+.B TARGETS
+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
+.BI "-E, --rename-chain " "old-chain new-chain"
+Rename the user specified chain to the user supplied name.  This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+.B -h
+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
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR icmp ,
+or
+.IR all ,
+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
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a network name, a hostname (please note that specifying
+any name to be resolved with a remote query such as DNS is a really bad idea),
+a network IP address (with /mask), or a plain IP address.
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 24
+is equivalent to
+.IR 255.255.255.0 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification. 
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax.  The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+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
+.B EXTENSIONS
+below).  If this
+option is omitted in a rule, then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BR "-i, --in-interface " "[!] \fIname\fP"
+Name of an interface via which a packet was received (only for
+packets entering the 
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+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
+.BR "-o, --out-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+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
+.B "[!] " "-f, --fragment"
+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
+.BI "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+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
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+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
+.B "-x, --exact"
+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 
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+.B "--modprobe=command"
+When adding or inserting rules into a chain, use
+.B command
+to load any necessary modules (targets, match extensions, etc).
+.SH MATCH EXTENSIONS
+iptables can use extended packet matching modules.  These are loaded
+in two ways: implicitly, when
+.B -p
+or
+.B --protocol
+is specified, or with the
+.B -m
+or
+.B --match
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module.  You can specify multiple extended match modules in one line,
+and you can use the
+.B -h
+or
+.B --help
+options after the module has been specified to receive help specific
+to that module.
+
+The following are included in the base package, and most of these can
+be preceded by a
+.B !
+to invert the sense of the match.
+.\" @MATCH@
+.SH TARGET EXTENSIONS
+iptables can use extended target modules: the following are included
+in the standard distribution.
+.\" @TARGET@
+.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, you might want to have a look at http://bugzilla.netfilter.org/
+.SH COMPATIBILITY WITH IPCHAINS
+This
+.B iptables
+is very similar to ipchains by Rusty Russell.  The main difference is
+that the chains
+.B INPUT
+and
+.B OUTPUT
+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
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.PP The various forms of NAT have been separated out; 
+.B iptables 
+is a pure packet filter when using the default `filter' table, with
+optional extension modules.  This should simplify much of the previous
+confusion over the combination of IP masquerading and packet filtering
+seen previously.  So the following options are handled differently:
+.nf
+ -j MASQ
+ -M -S
+ -M -L
+.fi
+There are several other changes in iptables.
+.SH SEE ALSO
+.BR iptables-save (8),
+.BR iptables-restore (8),
+.BR ip6tables (8),
+.BR ip6tables-save (8),
+.BR ip6tables-restore (8).
+.P
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+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 target, TTL, DSCP, ECN matches and targets.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Jozsef Kadlecsik, 
+Patrick McHardy, James Morris, Harald Welte and Rusty Russell.
+.PP
+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 ..
index 5863ef4..92c6c49 100644 (file)
 #define FALSE 0
 #endif
 
-#ifndef IPT_LIB_DIR
-#define IPT_LIB_DIR "/usr/lib/iptables"
-#endif
-
 #ifndef PROC_SYS_MODPROBE
 #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
 #endif
@@ -105,7 +101,7 @@ static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
 #define OPT_COUNTERS   0x00800U
 #define NUMBER_OF_OPT  12
 static const char optflags[NUMBER_OF_OPT]
-= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'};
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '0', 'c'};
 
 static struct option original_opts[] = {
        { "append", 1, 0, 'A' },
@@ -147,14 +143,6 @@ static struct option original_opts[] = {
  * magic number of -1 */
 int line = -1;
 
-#ifndef __OPTIMIZE__
-struct ipt_entry_target *
-ipt_get_target(struct ipt_entry *e)
-{
-       return (void *)e + e->target_offset;
-}
-#endif
-
 static struct option *opts = original_opts;
 static unsigned int global_option_offset = 0;
 
@@ -203,6 +191,7 @@ static int inverse_for_options[NUMBER_OF_OPT] =
 
 const char *program_version;
 const char *program_name;
+char *lib_dir;
 
 /* Keeping track of external matches and targets: linked lists.  */
 struct iptables_match *iptables_matches = NULL;
@@ -235,6 +224,7 @@ static const struct pprot chain_protos[] = {
        { "icmp", IPPROTO_ICMP },
        { "esp", IPPROTO_ESP },
        { "ah", IPPROTO_AH },
+       { "sctp", IPPROTO_SCTP },
        { "all", 0 },
 };
 
@@ -268,6 +258,7 @@ dotted_to_addr(const char *dotted)
 
        /* copy dotted string, because we need to modify it */
        strncpy(buf, dotted, sizeof(buf) - 1);
+       buf[sizeof(buf) - 1] = '\0';
        addrp = (unsigned char *) &(addr.s_addr);
 
        p = buf;
@@ -315,6 +306,16 @@ inaddrcpy(struct in_addr *dst, struct in_addr *src)
        dst->s_addr = src->s_addr;
 }
 
+static void free_opts(int reset_offset)
+{
+       if (opts != original_opts) {
+               free(opts);
+               opts = original_opts;
+               if (reset_offset)
+                       global_option_offset = 0;
+       }
+}
+
 void
 exit_error(enum exittype status, char *msg, ...)
 {
@@ -330,6 +331,8 @@ exit_error(enum exittype status, char *msg, ...)
        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 */
+       free_opts(1);
        exit(status);
 }
 
@@ -340,13 +343,14 @@ exit_tryhelp(int status)
                fprintf(stderr, "Error occurred at line: %d\n", line);
        fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
                        program_name, program_name );
+       free_opts(1);
        exit(status);
 }
 
 void
-exit_printhelp(void)
+exit_printhelp(struct iptables_rule_match *matches)
 {
-       struct iptables_match *m = NULL;
+       struct iptables_rule_match *matchp = NULL;
        struct iptables_target *t = NULL;
 
        printf("%s v%s\n\n"
@@ -411,14 +415,16 @@ exit_printhelp(void)
 
        /* 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=iptables_targets;t;t=t->next) {
-               printf("\n");
-               t->help();
+          results. So we call help for all specified matches & targets */
+       for (t = iptables_targets; t ;t = t->next) {
+               if (t->used) {
+                       printf("\n");
+                       t->help();
+               }
        }
-       for (m=iptables_matches;m;m=m->next) {
+       for (matchp = matches; matchp; matchp = matchp->next) {
                printf("\n");
-               m->help();
+               matchp->match->help();
        }
        exit(0);
 }
@@ -547,7 +553,7 @@ host_to_addr(const char *name, unsigned int *naddr)
 
                while (host->h_addr_list[*naddr] != (char *) NULL)
                        (*naddr)++;
-               addr = fw_calloc(*naddr, sizeof(struct in_addr));
+               addr = fw_calloc(*naddr, sizeof(struct in_addr) * *naddr);
                for (i = 0; i < *naddr; i++)
                        inaddrcpy(&(addr[i]),
                                  (struct in_addr *) host->h_addr_list[i]);
@@ -633,6 +639,7 @@ parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
        int i, j, k, n;
 
        strncpy(buf, name, sizeof(buf) - 1);
+       buf[sizeof(buf) - 1] = '\0';
        if ((p = strrchr(buf, '/')) != NULL) {
                *p = '\0';
                addrp = parse_mask(p + 1);
@@ -659,7 +666,7 @@ parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
 }
 
 struct iptables_match *
-find_match(const char *name, enum ipt_tryload tryload)
+find_match(const char *name, enum ipt_tryload tryload, struct iptables_rule_match **matches)
 {
        struct iptables_match *ptr;
 
@@ -670,13 +677,13 @@ find_match(const char *name, enum ipt_tryload tryload)
 
 #ifndef NO_SHARED_LIBS
        if (!ptr && tryload != DONT_LOAD) {
-               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+               char path[strlen(lib_dir) + sizeof("/libipt_.so")
                         + strlen(name)];
-               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               sprintf(path, "%s/libipt_%s.so", lib_dir, name);
                if (dlopen(path, RTLD_NOW)) {
                        /* Found library.  If it didn't register itself,
                           maybe they specified target as match. */
-                       ptr = find_match(name, DONT_LOAD);
+                       ptr = find_match(name, DONT_LOAD, NULL);
 
                        if (!ptr)
                                exit_error(PARAMETER_PROBLEM,
@@ -700,15 +707,24 @@ find_match(const char *name, enum ipt_tryload tryload)
        }
 #endif
 
-       if (ptr)
-               ptr->used = 1;
+       if (ptr && matches) {
+               struct iptables_rule_match **i;
+               struct iptables_rule_match *newentry;
+
+               newentry = fw_malloc(sizeof(struct iptables_rule_match));
+
+               for (i = matches; *i; i = &(*i)->next);
+               newentry->match = ptr;
+               newentry->next = NULL;
+               *i = newentry;
+       }
 
        return ptr;
 }
 
 /* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
 static struct iptables_match *
-find_proto(const char *pname, enum ipt_tryload tryload, int nolookup)
+find_proto(const char *pname, enum ipt_tryload tryload, int nolookup, struct iptables_rule_match **matches)
 {
        unsigned int proto;
 
@@ -716,9 +732,9 @@ find_proto(const char *pname, enum ipt_tryload tryload, int nolookup)
                char *protoname = proto_to_name(proto, nolookup);
 
                if (protoname)
-                       return find_match(protoname, tryload);
+                       return find_match(protoname, tryload, matches);
        } else
-               return find_match(pname, tryload);
+               return find_match(pname, tryload, matches);
 
        return NULL;
 }
@@ -753,8 +769,7 @@ parse_protocol(const char *s)
        return (u_int16_t)proto;
 }
 
-static void
-parse_interface(const char *arg, char *vianame, unsigned char *mask)
+void parse_interface(const char *arg, char *vianame, unsigned char *mask)
 {
        int vialen = strlen(arg);
        unsigned int i;
@@ -768,7 +783,7 @@ parse_interface(const char *arg, char *vianame, unsigned char *mask)
                           " (%i)", arg, IFNAMSIZ-1);
 
        strcpy(vianame, arg);
-       if (vialen == 0)
+       if ((vialen == 0) || (vialen == 1 && vianame[0] == '+'))
                memset(mask, 0, IFNAMSIZ);
        else if (vianame[vialen - 1] == '+') {
                memset(mask, 0xFF, vialen - 1);
@@ -815,8 +830,8 @@ parse_target(const char *targetname)
 
        if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
                exit_error(PARAMETER_PROBLEM,
-                          "Invalid target name `%s' (%i chars max)",
-                          targetname, sizeof(ipt_chainlabel)-1);
+                          "Invalid target name `%s' (%u chars max)",
+                          targetname, (unsigned int)sizeof(ipt_chainlabel)-1);
 
        for (ptr = targetname; *ptr; ptr++)
                if (isspace(*ptr))
@@ -886,18 +901,18 @@ mask_to_dotted(const struct in_addr *mask)
 }
 
 int
-string_to_number(const char *s, unsigned int min, unsigned int max,
-                unsigned int *ret)
+string_to_number_ll(const char *s, unsigned long long min, unsigned long long max,
+                unsigned long long *ret)
 {
-       long number;
+       unsigned long long number;
        char *end;
 
        /* Handle hex, octal, etc. */
        errno = 0;
-       number = strtol(s, &end, 0);
+       number = strtoull(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) {
+               if (errno != ERANGE && min <= number && (!max || number <= max)) {
                        *ret = number;
                        return 0;
                }
@@ -905,6 +920,31 @@ string_to_number(const char *s, unsigned int min, unsigned int max,
        return -1;
 }
 
+int
+string_to_number_l(const char *s, unsigned long min, unsigned long max,
+                unsigned long *ret)
+{
+       int result;
+       unsigned long long number;
+
+       result = string_to_number_ll(s, min, max, &number);
+       *ret = (unsigned long)number;
+
+       return result;
+}
+
+int string_to_number(const char *s, unsigned int min, unsigned int max,
+               unsigned int *ret)
+{
+       int result;
+       unsigned long number;
+
+       result = string_to_number_l(s, min, max, &number);
+       *ret = (unsigned int)number;
+
+       return result;
+}
+
 static void
 set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
           int invert)
@@ -946,9 +986,9 @@ find_target(const char *name, enum ipt_tryload tryload)
 
 #ifndef NO_SHARED_LIBS
        if (!ptr && tryload != DONT_LOAD) {
-               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+               char path[strlen(lib_dir) + sizeof("/libipt_.so")
                         + strlen(name)];
-               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               sprintf(path, "%s/libipt_%s.so", lib_dir, name);
                if (dlopen(path, RTLD_NOW)) {
                        /* Found library.  If it didn't register itself,
                           maybe they specified match as a target. */
@@ -988,6 +1028,9 @@ merge_options(struct option *oldopts, const struct option *newopts,
        unsigned int num_old, num_new, i;
        struct option *merge;
 
+       /* Release previous options merged if any */
+       free_opts(0);
+       
        for (num_old = 0; oldopts[num_old].name; num_old++);
        for (num_new = 0; newopts[num_new].name; num_new++);
 
@@ -1005,10 +1048,56 @@ merge_options(struct option *oldopts, const struct option *newopts,
        return merge;
 }
 
+static int compatible_revision(const char *name, u_int8_t revision, int opt)
+{
+       struct ipt_get_revision rev;
+       socklen_t s = sizeof(rev);
+       int max_rev, sockfd;
+
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0) {
+               fprintf(stderr, "Could not open socket to kernel: %s\n",
+                       strerror(errno));
+               exit(1);
+       }
+
+       strcpy(rev.name, name);
+       rev.revision = revision;
+
+       max_rev = getsockopt(sockfd, IPPROTO_IP, opt, &rev, &s);
+       if (max_rev < 0) {
+               /* Definitely don't support this? */
+               if (errno == EPROTONOSUPPORT) {
+                       close(sockfd);
+                       return 0;
+               } else if (errno == ENOPROTOOPT) {
+                       close(sockfd);
+                       /* Assume only revision 0 support (old kernel) */
+                       return (revision == 0);
+               } else {
+                       fprintf(stderr, "getsockopt failed strangely: %s\n",
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       close(sockfd);
+       return 1;
+}
+
+static int compatible_match_revision(const char *name, u_int8_t revision)
+{
+       return compatible_revision(name, revision, IPT_SO_GET_REVISION_MATCH);
+}
+
+static int compatible_target_revision(const char *name, u_int8_t revision)
+{
+       return compatible_revision(name, revision, IPT_SO_GET_REVISION_TARGET);
+}
+
 void
 register_match(struct iptables_match *me)
 {
-       struct iptables_match **i;
+       struct iptables_match **i, *old;
 
        if (strcmp(me->version, program_version) != 0) {
                fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
@@ -1016,15 +1105,39 @@ register_match(struct iptables_match *me)
                exit(1);
        }
 
-       if (find_match(me->name, DONT_LOAD)) {
-               fprintf(stderr, "%s: match `%s' already registered.\n",
+       /* Revision field stole a char from name. */
+       if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
+               fprintf(stderr, "%s: target `%s' has invalid name\n",
                        program_name, me->name);
                exit(1);
        }
 
+       old = find_match(me->name, DONT_LOAD, NULL);
+       if (old) {
+               if (old->revision == me->revision) {
+                       fprintf(stderr,
+                               "%s: match `%s' already registered.\n",
+                               program_name, me->name);
+                       exit(1);
+               }
+
+               /* Now we have two (or more) options, check compatibility. */
+               if (compatible_match_revision(old->name, old->revision)
+                   && old->revision > me->revision)
+                       return;
+
+               /* Replace if compatible. */
+               if (!compatible_match_revision(me->name, me->revision))
+                       return;
+
+               /* Delete old one. */
+               for (i = &iptables_matches; *i!=old; i = &(*i)->next);
+               *i = old->next;
+       }
+
        if (me->size != IPT_ALIGN(me->size)) {
                fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
-                       program_name, me->name, me->size);
+                       program_name, me->name, (unsigned int)me->size);
                exit(1);
        }
 
@@ -1040,21 +1153,49 @@ register_match(struct iptables_match *me)
 void
 register_target(struct iptables_target *me)
 {
+       struct iptables_target *old;
+
        if (strcmp(me->version, program_version) != 0) {
                fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
                        program_name, me->name, me->version, program_version);
                exit(1);
        }
 
-       if (find_target(me->name, DONT_LOAD)) {
-               fprintf(stderr, "%s: target `%s' already registered.\n",
+       /* Revision field stole a char from name. */
+       if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
+               fprintf(stderr, "%s: target `%s' has invalid name\n",
                        program_name, me->name);
                exit(1);
        }
 
+       old = find_target(me->name, DONT_LOAD);
+       if (old) {
+               struct iptables_target **i;
+
+               if (old->revision == me->revision) {
+                       fprintf(stderr,
+                               "%s: target `%s' already registered.\n",
+                               program_name, me->name);
+                       exit(1);
+               }
+
+               /* Now we have two (or more) options, check compatibility. */
+               if (compatible_target_revision(old->name, old->revision)
+                   && old->revision > me->revision)
+                       return;
+
+               /* Replace if compatible. */
+               if (!compatible_target_revision(me->name, me->revision))
+                       return;
+
+               /* Delete old one. */
+               for (i = &iptables_targets; *i!=old; i = &(*i)->next);
+               *i = old->next;
+       }
+
        if (me->size != IPT_ALIGN(me->size)) {
                fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
-                       program_name, me->name, me->size);
+                       program_name, me->name, (unsigned int)me->size);
                exit(1);
        }
 
@@ -1077,17 +1218,17 @@ print_num(u_int64_t number, unsigned int format)
                                        number = (number + 500) / 1000;
                                        if (number > 9999) {
                                                number = (number + 500) / 1000;
-                                               printf(FMT("%4lluT ","%lluT "), number);
+                                               printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
                                        }
-                                       else printf(FMT("%4lluG ","%lluG "), number);
+                                       else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
                                }
-                               else printf(FMT("%4lluM ","%lluM "), number);
+                               else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
                        } else
-                               printf(FMT("%4lluK ","%lluK "), number);
+                               printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
                } else
-                       printf(FMT("%5llu ","%llu "), number);
+                       printf(FMT("%5llu ","%llu "), (unsigned long long)number);
        } else
-               printf(FMT("%8llu ","%llu "), number);
+               printf(FMT("%8llu ","%llu "), (unsigned long long)number);
 }
 
 
@@ -1146,7 +1287,7 @@ print_match(const struct ipt_entry_match *m,
            const struct ipt_ip *ip,
            int numeric)
 {
-       struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD);
+       struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD, NULL);
 
        if (match) {
                if (match->print)
@@ -1275,7 +1416,7 @@ print_firewall(const struct ipt_entry *fw,
                        target->print(&fw->ip, t, format & FMT_NUMERIC);
        } else if (t->u.target_size != sizeof(*t))
                printf("[%u bytes of unknown target data] ",
-                      t->u.target_size - sizeof(*t));
+                      (unsigned int)(t->u.target_size - sizeof(*t)));
 
        if (!(format & FMT_NONEWLINE))
                fputc('\n', stdout);
@@ -1362,20 +1503,16 @@ insert_entry(const ipt_chainlabel chain,
 }
 
 static unsigned char *
-make_delete_mask(struct ipt_entry *fw)
+make_delete_mask(struct ipt_entry *fw, struct iptables_rule_match *matches)
 {
        /* Establish mask for comparison */
        unsigned int size;
-       struct iptables_match *m;
+       struct iptables_rule_match *matchp;
        unsigned char *mask, *mptr;
 
        size = sizeof(struct ipt_entry);
-       for (m = iptables_matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
-               size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
-       }
+       for (matchp = matches; matchp; matchp = matchp->next)
+               size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size;
 
        mask = fw_calloc(1, size
                         + IPT_ALIGN(sizeof(struct ipt_entry_target))
@@ -1384,14 +1521,11 @@ make_delete_mask(struct ipt_entry *fw)
        memset(mask, 0xFF, sizeof(struct ipt_entry));
        mptr = mask + sizeof(struct ipt_entry);
 
-       for (m = iptables_matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
+       for (matchp = matches; matchp; matchp = matchp->next) {
                memset(mptr, 0xFF,
                       IPT_ALIGN(sizeof(struct ipt_entry_match))
-                      + m->userspacesize);
-               mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+                      + matchp->match->userspacesize);
+               mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size;
        }
 
        memset(mptr, 0xFF,
@@ -1409,13 +1543,14 @@ delete_entry(const ipt_chainlabel chain,
             unsigned int ndaddrs,
             const struct in_addr daddrs[],
             int verbose,
-            iptc_handle_t *handle)
+            iptc_handle_t *handle,
+            struct iptables_rule_match *matches)
 {
        unsigned int i, j;
        int ret = 1;
        unsigned char *mask;
 
-       mask = make_delete_mask(fw);
+       mask = make_delete_mask(fw, matches);
        for (i = 0; i < nsaddrs; i++) {
                fw->ip.src.s_addr = saddrs[i].s_addr;
                for (j = 0; j < ndaddrs; j++) {
@@ -1425,6 +1560,8 @@ delete_entry(const ipt_chainlabel chain,
                        ret &= iptc_delete_entry(chain, fw, mask, handle);
                }
        }
+       free(mask);
+
        return ret;
 }
 
@@ -1455,7 +1592,7 @@ for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
        for (i = 0; i < chaincount; i++) {
                if (!builtinstoo
                    && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
-                                   *handle))
+                                   *handle) == 1)
                        continue;
                ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
        }
@@ -1558,15 +1695,17 @@ static char *get_modprobe(void)
        int procfile;
        char *ret;
 
+#define PROCFILE_BUFSIZ        1024
        procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
        if (procfile < 0)
                return NULL;
 
-       ret = malloc(1024);
+       ret = (char *) malloc(PROCFILE_BUFSIZ);
        if (ret) {
-               switch (read(procfile, ret, 1024)) {
+               memset(ret, 0, PROCFILE_BUFSIZ);
+               switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
                case -1: goto fail;
-               case 1024: goto fail; /* Partial read.  Wierd */
+               case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
                }
                if (ret[strlen(ret)-1]=='\n') 
                        ret[strlen(ret)-1]=0;
@@ -1583,7 +1722,7 @@ int iptables_insmod(const char *modname, const char *modprobe)
 {
        char *buf = NULL;
        char *argv[3];
-       int i=0;
+       int status;
 
        /* If they don't explicitly set it, read out of kernel */
        if (!modprobe) {
@@ -1595,44 +1734,38 @@ int iptables_insmod(const char *modname, const char *modprobe)
 
        switch (fork()) {
        case 0:
-               /* close open file descriptors */
-               for (i=0; i< 10; i++) {
-                 close(i);
-               }
                argv[0] = (char *)modprobe;
                argv[1] = (char *)modname;
                argv[2] = NULL;
                execv(argv[0], argv);
 
                /* not usually reached */
-               exit(0);
+               exit(1);
        case -1:
                return -1;
 
        default: /* parent */
-               wait(NULL);
+               wait(&status);
        }
 
        free(buf);
-       return 0;
+       if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+               return 0;
+       return -1;
 }
 
 static struct ipt_entry *
 generate_entry(const struct ipt_entry *fw,
-              struct iptables_match *matches,
+              struct iptables_rule_match *matches,
               struct ipt_entry_target *target)
 {
        unsigned int size;
-       struct iptables_match *m;
+       struct iptables_rule_match *matchp;
        struct ipt_entry *e;
 
        size = sizeof(struct ipt_entry);
-       for (m = matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
-               size += m->m->u.match_size;
-       }
+       for (matchp = matches; matchp; matchp = matchp->next)
+               size += matchp->match->m->u.match_size;
 
        e = fw_malloc(size + target->u.target_size);
        *e = *fw;
@@ -1640,18 +1773,38 @@ generate_entry(const struct ipt_entry *fw,
        e->next_offset = size + target->u.target_size;
 
        size = 0;
-       for (m = matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
-               memcpy(e->elems + size, m->m, m->m->u.match_size);
-               size += m->m->u.match_size;
+       for (matchp = matches; matchp; matchp = matchp->next) {
+               memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
+               size += matchp->match->m->u.match_size;
        }
        memcpy(e->elems + size, target, target->u.target_size);
 
        return e;
 }
 
+void clear_rule_matches(struct iptables_rule_match **matches)
+{
+       struct iptables_rule_match *matchp, *tmp;
+
+       for (matchp = *matches; matchp;) {
+               tmp = matchp->next;
+               if (matchp->match->m)
+                       free(matchp->match->m);
+               free(matchp);
+               matchp = tmp;
+       }
+
+       *matches = NULL;
+}
+
+static void set_revision(char *name, u_int8_t revision)
+{
+       /* Old kernel sources don't have ".revision" field,
+          but we stole a byte from name. */
+       name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
+       name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
 int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
 {
        struct ipt_entry fw, *e = NULL;
@@ -1667,6 +1820,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
        const char *pcnt = NULL, *bcnt = NULL;
        int ret = 1;
        struct iptables_match *m;
+       struct iptables_rule_match *matches = NULL;
+       struct iptables_rule_match *matchp;
        struct iptables_target *target = NULL;
        struct iptables_target *t;
        const char *jumpto = "";
@@ -1676,19 +1831,14 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
 
        memset(&fw, 0, sizeof(fw));
 
-       opts = original_opts;
-       global_option_offset = 0;
-
        /* re-set optind to 0 in case do_command gets called
         * a second time */
        optind = 0;
 
        /* clear mflags in case do_command gets called a second time
         * (we clear the global list of all matches for security)*/
-       for (m = iptables_matches; m; m = m->next) {
+       for (m = iptables_matches; m; m = m->next)
                m->mflags = 0;
-               m->used = 0;
-       }
 
        for (t = iptables_targets; t; t = t->next) {
                t->tflags = 0;
@@ -1774,10 +1924,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        break;
 
                case 'N':
-                       if (optarg && *optarg == '-')
+                       if (optarg && (*optarg == '-' || *optarg == '!'))
                                exit_error(PARAMETER_PROBLEM,
                                           "chain name not allowed to start "
-                                          "with `-'\n");
+                                          "with `%c'\n", *optarg);
                        if (find_target(optarg, TRY_LOAD))
                                exit_error(PARAMETER_PROBLEM,
                                           "chain name may not clash "
@@ -1828,10 +1978,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                optarg = argv[optind];
 
                        /* iptables -p icmp -h */
-                       if (!iptables_matches && protocol)
-                               find_match(protocol, TRY_LOAD);
+                       if (!matches && protocol)
+                               find_match(protocol, TRY_LOAD, &matches);
 
-                       exit_printhelp();
+                       exit_printhelp(matches);
 
                        /*
                         * Option selection
@@ -1852,7 +2002,6 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                            && (fw.ip.invflags & IPT_INV_PROTO))
                                exit_error(PARAMETER_PROBLEM,
                                           "rule would never match protocol");
-                       fw.nfcache |= NFC_IP_PROTO;
                        break;
 
                case 's':
@@ -1860,7 +2009,6 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        set_option(&options, OPT_SOURCE, &fw.ip.invflags,
                                   invert);
                        shostnetworkmask = argv[optind-1];
-                       fw.nfcache |= NFC_IP_SRC;
                        break;
 
                case 'd':
@@ -1868,7 +2016,6 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
                                   invert);
                        dhostnetworkmask = argv[optind-1];
-                       fw.nfcache |= NFC_IP_DST;
                        break;
 
                case 'j':
@@ -1887,7 +2034,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                target->t = fw_calloc(1, size);
                                target->t->u.target_size = size;
                                strcpy(target->t->u.user.name, jumpto);
-                               target->init(target->t, &fw.nfcache);
+                               set_revision(target->t->u.user.name,
+                                            target->revision);
+                               if (target->init != NULL)
+                                       target->init(target->t, &fw.nfcache);
                                opts = merge_options(opts, target->extra_opts, &target->option_offset);
                        }
                        break;
@@ -1900,7 +2050,6 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        parse_interface(argv[optind-1],
                                        fw.ip.iniface,
                                        fw.ip.iniface_mask);
-                       fw.nfcache |= NFC_IP_IF_IN;
                        break;
 
                case 'o':
@@ -1910,14 +2059,12 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        parse_interface(argv[optind-1],
                                        fw.ip.outiface,
                                        fw.ip.outiface_mask);
-                       fw.nfcache |= NFC_IP_IF_OUT;
                        break;
 
                case 'f':
                        set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
                                   invert);
                        fw.ip.flags |= IPT_F_FRAG;
-                       fw.nfcache |= NFC_IP_FRAG;
                        break;
 
                case 'v':
@@ -1934,13 +2081,15 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                exit_error(PARAMETER_PROBLEM,
                                           "unexpected ! flag before --match");
 
-                       m = find_match(optarg, LOAD_MUST_SUCCEED);
+                       m = find_match(optarg, LOAD_MUST_SUCCEED, &matches);
                        size = IPT_ALIGN(sizeof(struct ipt_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);
+                       set_revision(m->m->u.user.name, m->revision);
+                       if (m->init != NULL)
+                               m->init(m->m, &fw.nfcache);
                        opts = merge_options(opts, m->extra_opts, &m->option_offset);
                }
                break;
@@ -1992,12 +2141,12 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                        "-%c requires packet and byte counter",
                                        opt2char(OPT_COUNTERS));
 
-                       if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+                       if (sscanf(pcnt, "%llu", (unsigned long long *)&fw.counters.pcnt) != 1)
                                exit_error(PARAMETER_PROBLEM,
                                        "-%c packet counter not numeric",
                                        opt2char(OPT_COUNTERS));
 
-                       if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+                       if (sscanf(bcnt, "%llu", (unsigned long long *)&fw.counters.bcnt) != 1)
                                exit_error(PARAMETER_PROBLEM,
                                        "-%c byte counter not numeric",
                                        opt2char(OPT_COUNTERS));
@@ -2026,18 +2175,16 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                               argv, invert,
                                               &target->tflags,
                                               &fw, &target->t))) {
-                               for (m = iptables_matches; m; m = m->next) {
-                                       if (!m->used)
-                                               continue;
-
-                                       if (m->parse(c - m->option_offset,
+                               for (matchp = matches; matchp; matchp = matchp->next) {
+                                       if (matchp->match->parse(c - matchp->match->option_offset,
                                                     argv, invert,
-                                                    &m->mflags,
+                                                    &matchp->match->mflags,
                                                     &fw,
                                                     &fw.nfcache,
-                                                    &m->m))
+                                                    &matchp->match->m))
                                                break;
                                }
+                               m = matchp ? matchp->match : NULL;
 
                                /* If you listen carefully, you can
                                   actually hear this code suck. */
@@ -2065,13 +2212,13 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                if (m == NULL
                                    && protocol
                                    && (!find_proto(protocol, DONT_LOAD,
-                                                  options&OPT_NUMERIC) 
+                                                  options&OPT_NUMERIC, NULL
                                        || (find_proto(protocol, DONT_LOAD,
-                                                       options&OPT_NUMERIC)
+                                                       options&OPT_NUMERIC, NULL)
                                            && (proto_used == 0))
                                       )
                                    && (m = find_proto(protocol, TRY_LOAD,
-                                                      options&OPT_NUMERIC))) {
+                                                      options&OPT_NUMERIC, &matches))) {
                                        /* Try loading protocol */
                                        size_t size;
                                        
@@ -2083,7 +2230,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                        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);
+                                       set_revision(m->m->u.user.name,
+                                                    m->revision);
+                                       if (m->init != NULL)
+                                               m->init(m->m, &fw.nfcache);
 
                                        opts = merge_options(opts,
                                            m->extra_opts, &m->option_offset);
@@ -2100,12 +2250,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                invert = FALSE;
        }
 
-       for (m = iptables_matches; m; m = m->next) {
-               if (!m->used)
-                       continue;
-
-               m->final_check(m->mflags);
-       }
+       for (matchp = matches; matchp; matchp = matchp->next)
+               matchp->match->final_check(matchp->match->mflags);
 
        if (target)
                target->final_check(target->tflags);
@@ -2156,11 +2302,9 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
        if (!*handle)
                *handle = iptc_init(*table);
 
-       if (!*handle) {
-               /* try to insmod the module if iptc_init failed */
-               iptables_insmod("ip_tables", modprobe);
+       /* try to insmod the module if iptc_init failed */
+       if (!*handle && iptables_insmod("ip_tables", modprobe) != -1)
                *handle = iptc_init(*table);
-       }
 
        if (!*handle)
                exit_error(VERSION_PROBLEM,
@@ -2195,6 +2339,9 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        printf("Warning: using chain %s, not extension\n",
                               jumpto);
 
+                       if (target->t)
+                               free(target->t);
+
                        target = NULL;
                }
 
@@ -2213,7 +2360,9 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        target->t = fw_calloc(1, size);
                        target->t->u.target_size = size;
                        strcpy(target->t->u.user.name, jumpto);
-                       target->init(target->t, &fw.nfcache);
+                       set_revision(target->t->u.user.name, target->revision);
+                       if (target->init != NULL)
+                               target->init(target->t, &fw.nfcache);
                }
 
                if (!target) {
@@ -2223,7 +2372,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                         * chain. */
                        find_target(jumpto, LOAD_MUST_SUCCEED);
                } else {
-                       e = generate_entry(&fw, iptables_matches, target->t);
+                       e = generate_entry(&fw, matches, target->t);
+                       free(target->t);
                }
        }
 
@@ -2238,7 +2388,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                ret = delete_entry(chain, e,
                                   nsaddrs, saddrs, ndaddrs, daddrs,
                                   options&OPT_VERBOSE,
-                                  handle);
+                                  handle, matches);
                break;
        case CMD_DELETE_NUM:
                ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
@@ -2299,5 +2449,16 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
        if (verbose > 1)
                dump_entries(*handle);
 
+       clear_rule_matches(&matches);
+
+       if (e != NULL) {
+               free(e);
+               e = NULL;
+       }
+
+       free(saddrs);
+       free(daddrs);
+       free_opts(1);
+
        return ret;
 }
index 08502dc..6e745b3 100644 (file)
@@ -1,6 +1,6 @@
 .TH IPQ_CREATE_HANDLE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
 .\"
-\" $Id: ipq_create_handle.3,v 1.3 2001/11/24 15:09:20 jamesm Exp $
+\" $Id: ipq_create_handle.3 1056 2001-11-24 15:09:19Z jamesm $
 .\"
 .\"     Copyright (c) 2000-2001 Netfilter Core Team
 .\"
index baa00ec..6e29b6d 100644 (file)
@@ -1,6 +1,6 @@
 .TH IPQ_ERRSTR 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
 .\"
-.\" $Id: ipq_errstr.3,v 1.2 2001/10/16 14:41:02 jamesm Exp $
+.\" $Id: ipq_errstr.3 1041 2001-10-16 14:41:02Z jamesm $
 .\"
 .\"     Copyright (c) 2000 Netfilter Core Team
 .\"
index 97d15eb..9c079d8 100644 (file)
@@ -1,6 +1,6 @@
 .TH IPQ_MESSAGE_TYPE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
 .\"
-.\" $Id: ipq_message_type.3,v 1.2 2001/10/16 14:41:02 jamesm Exp $
+.\" $Id: ipq_message_type.3 1041 2001-10-16 14:41:02Z jamesm $
 .\"
 .\"     Copyright (c) 2000-2001 Netfilter Core Team
 .\"
index afdf5a9..d21d203 100644 (file)
@@ -1,6 +1,6 @@
 .TH IPQ_READ 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
 .\"
-.\" $Id: ipq_read.3,v 1.3 2001/10/16 16:58:25 jamesm Exp $
+.\" $Id: ipq_read.3 1042 2001-10-16 16:58:25Z jamesm $
 .\"
 .\"     Copyright (c) 2000-2001 Netfilter Core Team
 .\"
index 19b8856..de275ef 100644 (file)
@@ -1,6 +1,6 @@
 .TH IPQ_SET_MODE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
 .\"
-.\" $Id: ipq_set_mode.3,v 1.2 2001/10/16 14:41:02 jamesm Exp $
+.\" $Id: ipq_set_mode.3 1041 2001-10-16 14:41:02Z jamesm $
 .\"
 .\"     Copyright (c) 2000-2001 Netfilter Core Team
 .\"
index bdccb8f..004e673 100644 (file)
@@ -1,6 +1,6 @@
 .TH IPQ_SET_VERDICT 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
 .\"
-.\" $Id: ipq_set_verdict.3,v 1.2 2001/10/16 14:41:02 jamesm Exp $
+.\" $Id: ipq_set_verdict.3 1041 2001-10-16 14:41:02Z jamesm $
 .\"
 .\"     Copyright (c) 2000-2001 Netfilter Core Team
 .\"
index 1a0984d..033bde0 100644 (file)
@@ -1,6 +1,6 @@
 .TH LIBIPQ 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
 .\"
-.\" $Id: libipq.3,v 1.5 2001/11/24 15:09:20 jamesm Exp $
+.\" $Id: libipq.3 1056 2001-11-24 15:09:19Z jamesm $
 .\"
 .\"     Copyright (c) 2000-2001 Netfilter Core Team
 .\"
index 76a8281..2e8647c 100644 (file)
@@ -129,8 +129,8 @@ dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
        size_t i;
        STRUCT_ENTRY_TARGET *t;
 
-       printf("Entry %u (%lu):\n", entry2index(handle, e),
-              entry2offset(handle, e));
+       printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+              iptcb_entry2offset(handle, e));
        printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
               IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
        printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
@@ -145,21 +145,10 @@ dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
        printf("Flags: %02X\n", e->ip.flags);
        printf("Invflags: %02X\n", e->ip.invflags);
        printf("Counters: %llu packets, %llu bytes\n",
-              e->counters.pcnt, e->counters.bcnt);
+              (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
        printf("Cache: %08X ", e->nfcache);
        if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
        if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
-       if (e->nfcache & NFC_IP_SRC) printf("IP_SRC ");
-       if (e->nfcache & NFC_IP_DST) printf("IP_DST ");
-       if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN ");
-       if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT ");
-       if (e->nfcache & NFC_IP_TOS) printf("IP_TOS ");
-       if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO ");
-       if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS ");
-       if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS ");
-       if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT ");
-       if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT ");
-       if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN ");
        printf("\n");
 
        IPT_MATCH_ITERATE(e, print_match);
@@ -184,11 +173,10 @@ dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
        return 0;
 }
 
-static int
+static unsigned char *
 is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
 {
        unsigned int i;
-       STRUCT_ENTRY_TARGET *ta, *tb;
        unsigned char *mptr;
 
        /* Always compare head structures: ignore mask here. */
@@ -199,45 +187,35 @@ is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
            || a->ip.proto != b->ip.proto
            || a->ip.flags != b->ip.flags
            || a->ip.invflags != b->ip.invflags)
-               return 0;
+               return NULL;
 
        for (i = 0; i < IFNAMSIZ; i++) {
                if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
-                       return 0;
+                       return NULL;
                if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
                    != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
-                       return 0;
+                       return NULL;
                if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
-                       return 0;
+                       return NULL;
                if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
                    != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
-                       return 0;
+                       return NULL;
        }
 
        if (a->nfcache != b->nfcache
            || a->target_offset != b->target_offset
            || a->next_offset != b->next_offset)
-               return 0;
+               return NULL;
 
        mptr = matchmask + sizeof(STRUCT_ENTRY);
        if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
-               return 0;
+               return NULL;
+       mptr += IPT_ALIGN(sizeof(struct ipt_entry_target));
 
-       ta = GET_TARGET((STRUCT_ENTRY *)a);
-       tb = GET_TARGET((STRUCT_ENTRY *)b);
-       if (ta->u.target_size != tb->u.target_size)
-               return 0;
-       if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
-               return 0;
-
-       mptr += sizeof(*ta);
-       if (target_different(ta->data, tb->data,
-                            ta->u.target_size - sizeof(*ta), mptr))
-               return 0;
-
-       return 1;
+       return mptr;
 }
 
+#if 0
 /***************************** DEBUGGING ********************************/
 static inline int
 unconditional(const struct ipt_ip *ip)
@@ -292,20 +270,20 @@ check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
                assert(t->verdict == -NF_DROP-1
                       || t->verdict == -NF_ACCEPT-1
                       || t->verdict == RETURN
-                      || t->verdict < (int)h->entries.size);
+                      || t->verdict < (int)h->entries->size);
 
                if (t->verdict >= 0) {
                        STRUCT_ENTRY *te = get_entry(h, t->verdict);
                        int idx;
 
-                       idx = entry2index(h, te);
+                       idx = iptcb_entry2index(h, te);
                        assert(strcmp(GET_TARGET(te)->u.user.name,
                                      IPT_ERROR_TARGET)
                               != 0);
                        assert(te != e);
 
                        /* Prior node must be error node, or this node. */
-                       assert(t->verdict == entry2offset(h, e)+e->next_offset
+                       assert(t->verdict == iptcb_entry2offset(h, e)+e->next_offset
                               || strcmp(GET_TARGET(index2entry(h, idx-1))
                                         ->u.user.name, IPT_ERROR_TARGET)
                               == 0);
@@ -518,3 +496,5 @@ do_check(TC_HANDLE_T h, unsigned int line)
                      ERROR_TARGET) == 0);
 }
 #endif /*IPTC_DEBUG*/
+
+#endif
index f7947db..8ca5ea6 100644 (file)
@@ -136,8 +136,8 @@ dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
        int len;
        struct ip6t_entry_target *t;
        
-       printf("Entry %u (%lu):\n", entry2index(handle, e),
-              entry2offset(handle, e));
+       printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+              iptcb_entry2offset(handle, e));
        puts("SRC IP: ");
        inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
        puts(buf);
@@ -176,21 +176,10 @@ dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
        printf("Flags: %02X\n", e->ipv6.flags);
        printf("Invflags: %02X\n", e->ipv6.invflags);
        printf("Counters: %llu packets, %llu bytes\n",
-              e->counters.pcnt, e->counters.bcnt);
+              (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
        printf("Cache: %08X ", e->nfcache);
        if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
        if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
-       if (e->nfcache & NFC_IP6_SRC) printf("IP6_SRC ");
-       if (e->nfcache & NFC_IP6_DST) printf("IP6_DST ");
-       if (e->nfcache & NFC_IP6_IF_IN) printf("IP6_IF_IN ");
-       if (e->nfcache & NFC_IP6_IF_OUT) printf("IP6_IF_OUT ");
-       if (e->nfcache & NFC_IP6_TOS) printf("IP6_TOS ");
-       if (e->nfcache & NFC_IP6_PROTO) printf("IP6_PROTO ");
-       if (e->nfcache & NFC_IP6_OPTIONS) printf("IP6_OPTIONS ");
-       if (e->nfcache & NFC_IP6_TCPFLAGS) printf("IP6_TCPFLAGS ");
-       if (e->nfcache & NFC_IP6_SRC_PT) printf("IP6_SRC_PT ");
-       if (e->nfcache & NFC_IP6_DST_PT) printf("IP6_DST_PT ");
-       if (e->nfcache & NFC_IP6_PROTO_UNKNOWN) printf("IP6_PROTO_UNKNOWN ");
        printf("\n");
        
        IP6T_MATCH_ITERATE(e, print_match);
@@ -214,12 +203,11 @@ dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
        return 0;
 }
 
-static int
+static unsigned char *
 is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
        unsigned char *matchmask)
 {
        unsigned int i;
-       STRUCT_ENTRY_TARGET *ta, *tb;
        unsigned char *mptr;
 
        /* Always compare head structures: ignore mask here. */
@@ -231,43 +219,32 @@ is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
            || a->ipv6.tos != b->ipv6.tos
            || a->ipv6.flags != b->ipv6.flags
            || a->ipv6.invflags != b->ipv6.invflags)
-               return 0;
+               return NULL;
 
        for (i = 0; i < IFNAMSIZ; i++) {
                if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
-                       return 0;
+                       return NULL;
                if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
                    != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
-                       return 0;
+                       return NULL;
                if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
-                       return 0;
+                       return NULL;
                if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
                    != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
-                       return 0;
+                       return NULL;
        }
 
        if (a->nfcache != b->nfcache
            || a->target_offset != b->target_offset
            || a->next_offset != b->next_offset)
-               return 0;
+               return NULL;
 
        mptr = matchmask + sizeof(STRUCT_ENTRY);
        if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
-               return 0;
-
-       ta = GET_TARGET((STRUCT_ENTRY *)a);
-       tb = GET_TARGET((STRUCT_ENTRY *)b);
-       if (ta->u.target_size != tb->u.target_size)
-               return 0;
-       if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
-               return 0;
-       mptr += sizeof(*ta);
-
-       if (target_different(ta->data, tb->data,
-                            ta->u.target_size - sizeof(*ta), mptr))
-               return 0;
+               return NULL;
+       mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_target));
 
-       return 1;
+       return mptr;
 }
 
 /* All zeroes == unconditional rule. */
@@ -428,8 +405,8 @@ do_check(TC_HANDLE_T h, unsigned int line)
                assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
 
                /* Hooks and underflows must be valid entries */
-               entry2index(h, get_entry(h, h->info.hook_entry[i]));
-               entry2index(h, get_entry(h, h->info.underflow[i]));
+               iptcb_entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               iptcb_entry2index(h, get_entry(h, h->info.underflow[i]));
        }
 
        assert(h->info.size
index 3b2831f..88d23d0 100644 (file)
@@ -1,4 +1,4 @@
-/* Library which manipulates firewall rules.  Version $Revision: 1.41 $ */
+/* Library which manipulates firewall rules.  Version $Revision: 3756 $ */
 
 /* Architecture of firewall rules is as follows:
  *
@@ -10,7 +10,7 @@
 
 /* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
  * COPYING for details). 
- * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2000-2004 by the Netfilter Core Team <coreteam@netfilter.org>
  *
  * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
  *     - Reimplementation of chain cache to use offsets instead of entries
  *     - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
  *       don't rebuild the chain cache after every operation, instead fix it
  *       up after a ruleset change.  
+ * 2004-Aug-18: Harald Welte <laforge@netfilter.org>:
+ *     - futher performance work: total reimplementation of libiptc.
+ *     - libiptc now has a real internal (linked-list) represntation of the
+ *       ruleset and a parser/compiler from/to this internal representation
+ *     - again sponsored by Astaro AG (http://www.astaro.com/)
  */
+#include <sys/types.h>
+#include <sys/socket.h>
 
-#ifndef IPT_LIB_DIR
-#define IPT_LIB_DIR "/usr/lib/iptables"
+#include "linux_list.h"
+
+//#define IPTC_DEBUG2 1
+
+#ifdef IPTC_DEBUG2
+#include <fcntl.h>
+#define DEBUGP(x, args...)     fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
+#define DEBUGP_C(x, args...)   fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#define DEBUGP_C(x, args...)
 #endif
 
-#ifndef __OPTIMIZE__
-STRUCT_ENTRY_TARGET *
-GET_TARGET(STRUCT_ENTRY *e)
-{
-       return (void *)e + e->target_offset;
-}
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/local/lib/iptables"
 #endif
 
 static int sockfd = -1;
+static int sockfd_use = 0;
 static void *iptc_fn = NULL;
 
 static const char *hooknames[]
@@ -46,6 +59,16 @@ static const char *hooknames[]
 #endif
 };
 
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct chain_head;
+struct rule_head;
+
 struct counter_map
 {
        enum {
@@ -57,49 +80,92 @@ struct counter_map
        unsigned int mappos;
 };
 
-/* Convenience structures */
-struct ipt_error_target
+enum iptcc_rule_type {
+       IPTCC_R_STANDARD,               /* standard target (ACCEPT, ...) */
+       IPTCC_R_MODULE,                 /* extension module (SNAT, ...) */
+       IPTCC_R_FALLTHROUGH,            /* fallthrough rule */
+       IPTCC_R_JUMP,                   /* jump to other chain */
+};
+
+struct rule_head
 {
-       STRUCT_ENTRY_TARGET t;
-       char error[TABLE_MAXNAMELEN];
+       struct list_head list;
+       struct chain_head *chain;
+       struct counter_map counter_map;
+
+       unsigned int index;             /* index (needed for counter_map) */
+       unsigned int offset;            /* offset in rule blob */
+
+       enum iptcc_rule_type type;
+       struct chain_head *jump;        /* jump target, if IPTCC_R_JUMP */
+
+       unsigned int size;              /* size of entry data */
+       STRUCT_ENTRY entry[0];
 };
 
-struct chain_cache
+struct chain_head
 {
+       struct list_head list;
        char name[TABLE_MAXNAMELEN];
-       /* This is the first rule in chain. */
-       unsigned int start_off;
-       /* Last rule in chain */
-       unsigned int end_off;
+       unsigned int hooknum;           /* hook number+1 if builtin */
+       unsigned int references;        /* how many jumps reference us */
+       int verdict;                    /* verdict if builtin */
+
+       STRUCT_COUNTERS counters;       /* per-chain counters */
+       struct counter_map counter_map;
+
+       unsigned int num_rules;         /* number of rules in list */
+       struct list_head rules;         /* list of rules */
+
+       unsigned int index;             /* index (needed for jump resolval) */
+       unsigned int head_offset;       /* offset in rule blob */
+       unsigned int foot_index;        /* index (needed for counter_map) */
+       unsigned int foot_offset;       /* offset in rule blob */
 };
 
 STRUCT_TC_HANDLE
 {
-       /* Have changes been made? */
-       int changed;
-       /* Size in here reflects original state. */
+       int changed;                     /* Have changes been made? */
+
+       struct list_head chains;
+       
+       struct chain_head *chain_iterator_cur;
+       struct rule_head *rule_iterator_cur;
+
        STRUCT_GETINFO info;
+       STRUCT_GET_ENTRIES *entries;
+};
 
-       struct counter_map *counter_map;
-       /* Array of hook names */
-       const char **hooknames;
+/* allocate a new chain head for the cache */
+static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
+{
+       struct chain_head *c = malloc(sizeof(*c));
+       if (!c)
+               return NULL;
+       memset(c, 0, sizeof(*c));
 
-       /* Cached position of chain heads (NULL = no cache). */
-       unsigned int cache_num_chains;
-       unsigned int cache_num_builtins;
-       struct chain_cache *cache_chain_heads;
+       strncpy(c->name, name, TABLE_MAXNAMELEN);
+       c->hooknum = hooknum;
+       INIT_LIST_HEAD(&c->rules);
 
-       /* Chain iterator: current chain cache entry. */
-       struct chain_cache *cache_chain_iteration;
+       return c;
+}
 
-       /* Rule iterator: terminal rule */
-       STRUCT_ENTRY *cache_rule_end;
+/* allocate and initialize a new rule for the cache */
+static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
+{
+       struct rule_head *r = malloc(sizeof(*r)+size);
+       if (!r)
+               return NULL;
+       memset(r, 0, sizeof(*r));
 
-       /* Number in here reflects current state. */
-       unsigned int new_number;
-       STRUCT_GET_ENTRIES entries;
-};
+       r->chain = c;
+       r->size = size;
 
+       return r;
+}
+
+/* notify us that the ruleset has been modified by the user */
 static void
 set_changed(TC_HANDLE_T h)
 {
@@ -113,8 +179,13 @@ static void do_check(TC_HANDLE_T h, unsigned int line);
 #define CHECK(h)
 #endif
 
+
+/**********************************************************************
+ * iptc blob utility functions (iptcb_*)
+ **********************************************************************/
+
 static inline int
-get_number(const STRUCT_ENTRY *i,
+iptcb_get_number(const STRUCT_ENTRY *i,
           const STRUCT_ENTRY *seek,
           unsigned int *pos)
 {
@@ -124,22 +195,8 @@ get_number(const STRUCT_ENTRY *i,
        return 0;
 }
 
-static unsigned int
-entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
-{
-       unsigned int pos = 0;
-
-       if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
-                         get_number, seek, &pos) == 0) {
-               fprintf(stderr, "ERROR: offset %i not an entry!\n",
-                       (char *)seek - (char *)h->entries.entrytable);
-               abort();
-       }
-       return pos;
-}
-
 static inline int
-get_entry_n(STRUCT_ENTRY *i,
+iptcb_get_entry_n(STRUCT_ENTRY *i,
            unsigned int number,
            unsigned int *pos,
            STRUCT_ENTRY **pe)
@@ -152,64 +209,556 @@ get_entry_n(STRUCT_ENTRY *i,
        return 0;
 }
 
-static STRUCT_ENTRY *
-index2entry(TC_HANDLE_T h, unsigned int index)
+static inline STRUCT_ENTRY *
+iptcb_get_entry(TC_HANDLE_T h, unsigned int offset)
 {
-       unsigned int pos = 0;
-       STRUCT_ENTRY *ret = NULL;
+       return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
+}
 
-       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
-                     get_entry_n, index, &pos, &ret);
+static unsigned int
+iptcb_entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+{
+       unsigned int pos = 0;
 
-       return ret;
+       if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+                         iptcb_get_number, seek, &pos) == 0) {
+               fprintf(stderr, "ERROR: offset %u not an entry!\n",
+                       (unsigned int)((char *)seek - (char *)h->entries->entrytable));
+               abort();
+       }
+       return pos;
 }
 
 static inline STRUCT_ENTRY *
-get_entry(TC_HANDLE_T h, unsigned int offset)
+iptcb_offset2entry(TC_HANDLE_T h, unsigned int offset)
 {
-       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+       return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
 }
 
+
 static inline unsigned long
-entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+iptcb_entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
 {
-       return (void *)e - (void *)h->entries.entrytable;
+       return (void *)e - (void *)h->entries->entrytable;
 }
 
-static inline unsigned long
-index2offset(TC_HANDLE_T h, unsigned int index)
+static inline unsigned int
+iptcb_offset2index(const TC_HANDLE_T h, unsigned int offset)
 {
-       return entry2offset(h, index2entry(h, index));
+       return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
 }
 
-static inline STRUCT_ENTRY *
-offset2entry(TC_HANDLE_T h, unsigned int offset)
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
 {
-       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((h->info.valid_hooks & (1 << i))
+                   && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
+                       return i+1;
+       }
+       return 0;
 }
 
-static inline unsigned int
-offset2index(const TC_HANDLE_T h, unsigned int offset)
+
+/**********************************************************************
+ * iptc cache utility functions (iptcc_*)
+ **********************************************************************/
+
+/* Is the given chain builtin (1) or user-defined (0) */
+static unsigned int iptcc_is_builtin(struct chain_head *c)
 {
-       return entry2index(h, offset2entry(h, offset));
+       return (c->hooknum ? 1 : 0);
 }
 
+/* Get a specific rule within a chain */
+static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
+                                           unsigned int rulenum)
+{
+       struct rule_head *r;
+       unsigned int num = 0;
+
+       list_for_each_entry(r, &c->rules, list) {
+               num++;
+               if (num == rulenum)
+                       return r;
+       }
+       return NULL;
+}
 
-static const char *
-get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+/* Get a specific rule within a chain backwards */
+static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
+                                           unsigned int rulenum)
 {
-       STRUCT_ENTRY *e;
+       struct rule_head *r;
+       unsigned int num = 0;
 
-       e = get_entry(h, offset);
-       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
-               fprintf(stderr, "ERROR: offset %u not an error node!\n",
-                       offset);
-               abort();
+       list_for_each_entry_reverse(r, &c->rules, list) {
+               num++;
+               if (num == rulenum)
+                       return r;
+       }
+       return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
+{
+       struct list_head *pos;
+
+       if (list_empty(&handle->chains))
+               return NULL;
+
+       list_for_each(pos, &handle->chains) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (offset >= c->head_offset && offset <= c->foot_offset)
+                       return c;
+       }
+
+       return NULL;
+}
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_label(const char *name, TC_HANDLE_T handle)
+{
+       struct list_head *pos;
+
+       if (list_empty(&handle->chains))
+               return NULL;
+
+       list_for_each(pos, &handle->chains) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (!strcmp(c->name, name))
+                       return c;
+       }
+
+       return NULL;
+}
+
+/* called when rule is to be removed from cache */
+static void iptcc_delete_rule(struct rule_head *r)
+{
+       DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
+       /* clean up reference count of called chain */
+       if (r->type == IPTCC_R_JUMP
+           && r->jump)
+               r->jump->references--;
+
+       list_del(&r->list);
+       free(r);
+}
+
+
+/**********************************************************************
+ * RULESET PARSER (blob -> cache)
+ **********************************************************************/
+
+/* Delete policy rule of previous chain, since cache doesn't contain
+ * chain policy rules.
+ * WARNING: This function has ugly design and relies on a lot of context, only
+ * to be called from specific places within the parser */
+static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
+{
+       if (h->chain_iterator_cur) {
+               /* policy rule is last rule */
+               struct rule_head *pr = (struct rule_head *)
+                       h->chain_iterator_cur->rules.prev;
+
+               /* save verdict */
+               h->chain_iterator_cur->verdict = 
+                       *(int *)GET_TARGET(pr->entry)->data;
+
+               /* save counter and counter_map information */
+               h->chain_iterator_cur->counter_map.maptype = 
+                                               COUNTER_MAP_NORMAL_MAP;
+               h->chain_iterator_cur->counter_map.mappos = num-1;
+               memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters, 
+                       sizeof(h->chain_iterator_cur->counters));
+
+               /* foot_offset points to verdict rule */
+               h->chain_iterator_cur->foot_index = num;
+               h->chain_iterator_cur->foot_offset = pr->offset;
+
+               /* delete rule from cache */
+               iptcc_delete_rule(pr);
+               h->chain_iterator_cur->num_rules--;
+
+               return 1;
+       }
+       return 0;
+}
+
+/* alphabetically insert a chain into the list */
+static inline void iptc_insert_chain(TC_HANDLE_T h, struct chain_head *c)
+{
+       struct chain_head *tmp;
+
+       /* sort only user defined chains */
+       if (!c->hooknum) {
+               list_for_each_entry(tmp, &h->chains, list) {
+                       if (strcmp(c->name, tmp->name) <= 0) {
+                               list_add(&c->list, tmp->list.prev);
+                               return;
+                       }
+               }
+       }
+
+       /* survived till end of list: add at tail */
+       list_add_tail(&c->list, &h->chains);
+}
+
+/* Another ugly helper function split out of cache_add_entry to make it less
+ * spaghetti code */
+static void __iptcc_p_add_chain(TC_HANDLE_T h, struct chain_head *c,
+                               unsigned int offset, unsigned int *num)
+{
+       __iptcc_p_del_policy(h, *num);
+
+       c->head_offset = offset;
+       c->index = *num;
+
+       iptc_insert_chain(h, c);
+       
+       h->chain_iterator_cur = c;
+}
+
+/* main parser function: add an entry from the blob to the cache */
+static int cache_add_entry(STRUCT_ENTRY *e, 
+                          TC_HANDLE_T h, 
+                          STRUCT_ENTRY **prev,
+                          unsigned int *num)
+{
+       unsigned int builtin;
+       unsigned int offset = (char *)e - (char *)h->entries->entrytable;
+
+       DEBUGP("entering...");
+
+       /* Last entry ("policy rule"). End it.*/
+       if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
+               /* This is the ERROR node at the end of the chain */
+               DEBUGP_C("%u:%u: end of table:\n", *num, offset);
+
+               __iptcc_p_del_policy(h, *num);
+
+               h->chain_iterator_cur = NULL;
+               goto out_inc;
+       }
+
+       /* We know this is the start of a new chain if it's an ERROR
+        * target, or a hook entry point */
+
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+               struct chain_head *c = 
+                       iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
+               DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset, 
+                       (char *)c->name, c);
+               if (!c) {
+                       errno = -ENOMEM;
+                       return -1;
+               }
+
+               __iptcc_p_add_chain(h, c, offset, num);
+
+       } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
+               struct chain_head *c =
+                       iptcc_alloc_chain_head((char *)hooknames[builtin-1], 
+                                               builtin);
+               DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n", 
+                       *num, offset, c, &c->rules);
+               if (!c) {
+                       errno = -ENOMEM;
+                       return -1;
+               }
+
+               c->hooknum = builtin;
+
+               __iptcc_p_add_chain(h, c, offset, num);
+
+               /* FIXME: this is ugly. */
+               goto new_rule;
+       } else {
+               /* has to be normal rule */
+               struct rule_head *r;
+new_rule:
+
+               if (!(r = iptcc_alloc_rule(h->chain_iterator_cur, 
+                                          e->next_offset))) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+               DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
+
+               r->index = *num;
+               r->offset = offset;
+               memcpy(r->entry, e, e->next_offset);
+               r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+               r->counter_map.mappos = r->index;
+
+               /* handling of jumps, etc. */
+               if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
+                       STRUCT_STANDARD_TARGET *t;
+
+                       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+                       if (t->target.u.target_size
+                           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+
+                       if (t->verdict < 0) {
+                               DEBUGP_C("standard, verdict=%d\n", t->verdict);
+                               r->type = IPTCC_R_STANDARD;
+                       } else if (t->verdict == r->offset+e->next_offset) {
+                               DEBUGP_C("fallthrough\n");
+                               r->type = IPTCC_R_FALLTHROUGH;
+                       } else {
+                               DEBUGP_C("jump, target=%u\n", t->verdict);
+                               r->type = IPTCC_R_JUMP;
+                               /* Jump target fixup has to be deferred
+                                * until second pass, since we migh not
+                                * yet have parsed the target */
+                       }
+               } else {
+                       DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name);
+                       r->type = IPTCC_R_MODULE;
+               }
+
+               list_add_tail(&r->list, &h->chain_iterator_cur->rules);
+               h->chain_iterator_cur->num_rules++;
+       }
+out_inc:
+       (*num)++;
+       return 0;
+}
+
+
+/* parse an iptables blob into it's pieces */
+static int parse_table(TC_HANDLE_T h)
+{
+       STRUCT_ENTRY *prev;
+       unsigned int num = 0;
+       struct chain_head *c;
+
+       /* First pass: over ruleset blob */
+       ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+                       cache_add_entry, h, &prev, &num);
+
+       /* Second pass: fixup parsed data from first pass */
+       list_for_each_entry(c, &h->chains, list) {
+               struct rule_head *r;
+               list_for_each_entry(r, &c->rules, list) {
+                       struct chain_head *c;
+                       STRUCT_STANDARD_TARGET *t;
+
+                       if (r->type != IPTCC_R_JUMP)
+                               continue;
+
+                       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+                       c = iptcc_find_chain_by_offset(h, t->verdict);
+                       if (!c)
+                               return -1;
+                       r->jump = c;
+                       c->references++;
+               }
+       }
+
+       /* FIXME: sort chains */
+
+       return 1;
+}
+
+
+/**********************************************************************
+ * RULESET COMPILATION (cache -> blob)
+ **********************************************************************/
+
+/* Convenience structures */
+struct iptcb_chain_start{
+       STRUCT_ENTRY e;
+       struct ipt_error_target name;
+};
+#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(struct ipt_error_target)))
+
+struct iptcb_chain_foot {
+       STRUCT_ENTRY e;
+       STRUCT_STANDARD_TARGET target;
+};
+#define IPTCB_CHAIN_FOOT_SIZE  (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
+
+struct iptcb_chain_error {
+       STRUCT_ENTRY entry;
+       struct ipt_error_target target;
+};
+#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(struct ipt_error_target)))
+
+
+
+/* compile rule from cache into blob */
+static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r)
+{
+       /* handle jumps */
+       if (r->type == IPTCC_R_JUMP) {
+               STRUCT_STANDARD_TARGET *t;
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+               /* memset for memcmp convenience on delete/replace */
+               memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+               strcpy(t->target.u.user.name, STANDARD_TARGET);
+               /* Jumps can only happen to builtin chains, so we
+                * can safely assume that they always have a header */
+               t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
+       } else if (r->type == IPTCC_R_FALLTHROUGH) {
+               STRUCT_STANDARD_TARGET *t;
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+               t->verdict = r->offset + r->size;
+       }
+       
+       /* copy entry from cache to blob */
+       memcpy((char *)repl->entries+r->offset, r->entry, r->size);
+
+       return 1;
+}
+
+/* compile chain from cache into blob */
+static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
+{
+       int ret;
+       struct rule_head *r;
+       struct iptcb_chain_start *head;
+       struct iptcb_chain_foot *foot;
+
+       /* only user-defined chains have heaer */
+       if (!iptcc_is_builtin(c)) {
+               /* put chain header in place */
+               head = (void *)repl->entries + c->head_offset;
+               head->e.target_offset = sizeof(STRUCT_ENTRY);
+               head->e.next_offset = IPTCB_CHAIN_START_SIZE;
+               strcpy(head->name.t.u.user.name, ERROR_TARGET);
+               head->name.t.u.target_size = 
+                               ALIGN(sizeof(struct ipt_error_target));
+               strcpy(head->name.error, c->name);
+       } else {
+               repl->hook_entry[c->hooknum-1] = c->head_offset;        
+               repl->underflow[c->hooknum-1] = c->foot_offset;
+       }
+
+       /* iterate over rules */
+       list_for_each_entry(r, &c->rules, list) {
+               ret = iptcc_compile_rule(h, repl, r);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* put chain footer in place */
+       foot = (void *)repl->entries + c->foot_offset;
+       foot->e.target_offset = sizeof(STRUCT_ENTRY);
+       foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
+       strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
+       foot->target.target.u.target_size =
+                               ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       /* builtin targets have verdict, others return */
+       if (iptcc_is_builtin(c))
+               foot->target.verdict = c->verdict;
+       else
+               foot->target.verdict = RETURN;
+       /* set policy-counters */
+       memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
+
+       return 0;
+}
+
+/* calculate offset and number for every rule in the cache */
+static int iptcc_compile_chain_offsets(TC_HANDLE_T h, struct chain_head *c,
+                                      int *offset, int *num)
+{
+       struct rule_head *r;
+
+       c->head_offset = *offset;
+       DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
+
+       if (!iptcc_is_builtin(c))  {
+               /* Chain has header */
+               *offset += sizeof(STRUCT_ENTRY) 
+                            + ALIGN(sizeof(struct ipt_error_target));
+               (*num)++;
+       }
+
+       list_for_each_entry(r, &c->rules, list) {
+               DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
+               r->offset = *offset;
+               r->index = *num;
+               *offset += r->size;
+               (*num)++;
+       }
+
+       DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num, 
+               *offset, *num);
+       c->foot_offset = *offset;
+       c->foot_index = *num;
+       *offset += sizeof(STRUCT_ENTRY)
+                  + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       (*num)++;
+
+       return 1;
+}
+
+/* put the pieces back together again */
+static int iptcc_compile_table_prep(TC_HANDLE_T h, unsigned int *size)
+{
+       struct chain_head *c;
+       unsigned int offset = 0, num = 0;
+       int ret = 0;
+
+       /* First pass: calculate offset for every rule */
+       list_for_each_entry(c, &h->chains, list) {
+               ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Append one error rule at end of chain */
+       num++;
+       offset += sizeof(STRUCT_ENTRY)
+                 + ALIGN(sizeof(struct ipt_error_target));
+
+       /* ruleset size is now in offset */
+       *size = offset;
+       return num;
+}
+
+static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl)
+{
+       struct chain_head *c;
+       struct iptcb_chain_error *error;
+
+       /* Second pass: copy from cache to offsets, fill in jumps */
+       list_for_each_entry(c, &h->chains, list) {
+               int ret = iptcc_compile_chain(h, repl, c);
+               if (ret < 0)
+                       return ret;
        }
 
-       return (const char *)GET_TARGET(e)->data;
+       /* Append error rule at end of chain */
+       error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
+       error->entry.target_offset = sizeof(STRUCT_ENTRY);
+       error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
+       error->target.t.u.user.target_size = 
+               ALIGN(sizeof(struct ipt_error_target));
+       strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
+       strcpy((char *)&error->target.error, "ERROR");
+
+       return 1;
 }
 
+/**********************************************************************
+ * EXTERNAL API (operates on cache only)
+ **********************************************************************/
+
 /* Allocate handle of given size */
 static TC_HANDLE_T
 alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
@@ -217,113 +766,138 @@ alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
        size_t len;
        TC_HANDLE_T h;
 
-       len = sizeof(STRUCT_TC_HANDLE)
-               + size
-               + num_rules * sizeof(struct counter_map);
+       len = sizeof(STRUCT_TC_HANDLE) + size;
 
-       if ((h = malloc(len)) == NULL) {
+       h = malloc(sizeof(STRUCT_TC_HANDLE));
+       if (!h) {
                errno = ENOMEM;
                return NULL;
        }
-
-       h->changed = 0;
-       h->cache_num_chains = 0;
-       h->cache_chain_heads = NULL;
-       h->counter_map = (void *)h
-               + sizeof(STRUCT_TC_HANDLE)
-               + size;
+       memset(h, 0, sizeof(*h));
+       INIT_LIST_HEAD(&h->chains);
        strcpy(h->info.name, tablename);
-       strcpy(h->entries.name, tablename);
+
+       h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
+       if (!h->entries)
+               goto out_free_handle;
+
+       strcpy(h->entries->name, tablename);
+       h->entries->size = size;
 
        return h;
+
+out_free_handle:
+       free(h);
+
+       return NULL;
 }
 
+
 TC_HANDLE_T
 TC_INIT(const char *tablename)
 {
        TC_HANDLE_T h;
        STRUCT_GETINFO info;
-       unsigned int i;
        int tmp;
        socklen_t s;
 
        iptc_fn = TC_INIT;
 
-       if (sockfd != -1) {
-               close(sockfd);
-               sockfd = -1;
-       }
-
        if (strlen(tablename) >= TABLE_MAXNAMELEN) {
                errno = EINVAL;
                return NULL;
        }
        
-       sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
-       if (sockfd < 0)
-               return NULL;
+       if (sockfd_use == 0) {
+               sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+               if (sockfd < 0)
+                       return NULL;
+       }
+       sockfd_use++;
 
        s = sizeof(info);
 
        strcpy(info.name, tablename);
-       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {
+               if (--sockfd_use == 0) {
+                       close(sockfd);
+                       sockfd = -1;
+               }
                return NULL;
+       }
+
+       DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
+               info.valid_hooks, info.num_entries, info.size);
 
        if ((h = alloc_handle(info.name, info.size, info.num_entries))
            == NULL) {
-               close(sockfd);
-               sockfd = -1;
-               return NULL;
-       }
-
-/* Too hard --RR */
-#if 0
-       sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
-       dynlib = dlopen(pathname, RTLD_NOW);
-       if (!dynlib) {
-               errno = ENOENT;
-               return NULL;
-       }
-       h->hooknames = dlsym(dynlib, "hooknames");
-       if (!h->hooknames) {
-               errno = ENOENT;
+               if (--sockfd_use == 0) {
+                       close(sockfd);
+                       sockfd = -1;
+               }
                return NULL;
        }
-#else
-       h->hooknames = hooknames;
-#endif
 
        /* Initialize current state */
        h->info = info;
-       h->new_number = h->info.num_entries;
-       for (i = 0; i < h->info.num_entries; i++)
-               h->counter_map[i]
-                       = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
 
-       h->entries.size = h->info.size;
+       h->entries->size = h->info.size;
 
        tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
 
-       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
-                      &tmp) < 0) {
-               close(sockfd);
-               sockfd = -1;
-               free(h);
-               return NULL;
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
+                      &tmp) < 0)
+               goto error;
+
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_get_entries.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, h->entries, tmp);
+                       close(fd);
+               }
        }
+#endif
+
+       if (parse_table(h) < 0)
+               goto error;
 
        CHECK(h);
        return h;
+error:
+       if (--sockfd_use == 0) {
+               close(sockfd);
+               sockfd = -1;
+       }
+       TC_FREE(&h);
+       return NULL;
 }
 
 void
 TC_FREE(TC_HANDLE_T *h)
 {
-       close(sockfd);
-       sockfd = -1;
-       if ((*h)->cache_chain_heads)
-               free((*h)->cache_chain_heads);
+       struct chain_head *c, *tmp;
+
+       iptc_fn = TC_FREE;
+       if (--sockfd_use == 0) {
+               close(sockfd);
+               sockfd = -1;
+       }
+
+       list_for_each_entry_safe(c, tmp, &(*h)->chains, list) {
+               struct rule_head *r, *rtmp;
+
+               list_for_each_entry_safe(r, rtmp, &c->rules, list) {
+                       free(r);
+               }
+
+               free(c);
+       }
+
+       free((*h)->entries);
        free(*h);
+
        *h = NULL;
 }
 
@@ -339,11 +913,11 @@ static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
 void
 TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
 {
+       iptc_fn = TC_DUMP_ENTRIES;
        CHECK(handle);
-
-       printf("libiptc v%s.  %u entries, %u bytes.\n",
-              IPTABLES_VERSION,
-              handle->new_number, handle->entries.size);
+#if 0
+       printf("libiptc v%s. %u bytes.\n",
+              IPTABLES_VERSION, handle->entries->size);
        printf("Table `%s'\n", handle->info.name);
        printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
               handle->info.hook_entry[HOOK_PRE_ROUTING],
@@ -358,604 +932,277 @@ TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
               handle->info.underflow[HOOK_LOCAL_OUT],
               handle->info.underflow[HOOK_POST_ROUTING]);
 
-       ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
+       ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
                      dump_entry, handle);
-}
-
-/* Returns 0 if not hook entry, else hooknumber + 1 */
-static inline unsigned int
-is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
-{
-       unsigned int i;
-
-       for (i = 0; i < NUMHOOKS; i++) {
-               if ((h->info.valid_hooks & (1 << i))
-                   && get_entry(h, h->info.hook_entry[i]) == e)
-                       return i+1;
-       }
-       return 0;
-}
-
-static inline int
-add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
-{
-       unsigned int builtin;
-
-       /* Last entry.  End it. */
-       if (entry2offset(h, e) + e->next_offset == h->entries.size) {
-               /* This is the ERROR node at end of the table */
-               h->cache_chain_heads[h->cache_num_chains-1].end_off = 
-                       entry2offset(h, *prev);
-               return 0;
-       }
-
-       /* We know this is the start of a new chain if it's an ERROR
-          target, or a hook entry point */
-       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
-               /* prev was last entry in previous chain */
-               h->cache_chain_heads[h->cache_num_chains-1].end_off
-                       = entry2offset(h, *prev);
-
-               strcpy(h->cache_chain_heads[h->cache_num_chains].name,
-                      (const char *)GET_TARGET(e)->data);
-               h->cache_chain_heads[h->cache_num_chains].start_off
-                       = entry2offset(h, (void *)e + e->next_offset);
-               h->cache_num_chains++;
-       } else if ((builtin = is_hook_entry(e, h)) != 0) {
-               if (h->cache_num_chains > 0)
-                       /* prev was last entry in previous chain */
-                       h->cache_chain_heads[h->cache_num_chains-1].end_off
-                               = entry2offset(h, *prev);
-
-               strcpy(h->cache_chain_heads[h->cache_num_chains].name,
-                      h->hooknames[builtin-1]);
-               h->cache_chain_heads[h->cache_num_chains].start_off
-                       = entry2offset(h, (void *)e);
-               h->cache_num_chains++;
-       }
-
-       *prev = e;
-       return 0;
-}
-
-static int alphasort(const void *a, const void *b)
-{
-       return strcmp(((struct chain_cache *)a)->name,
-                     ((struct chain_cache *)b)->name);
-}
-
-static int populate_cache(TC_HANDLE_T h)
-{
-       unsigned int i;
-       STRUCT_ENTRY *prev;
-
-       /* # chains < # rules / 2 + num builtins - 1 */
-       h->cache_chain_heads = malloc((h->new_number / 2 + 4)
-                                     * sizeof(struct chain_cache));
-       if (!h->cache_chain_heads) {
-               errno = ENOMEM;
-               return 0;
-       }
-
-       h->cache_num_chains = 0;
-       h->cache_num_builtins = 0;
-
-       /* Count builtins */
-       for (i = 0; i < NUMHOOKS; i++) {
-               if (h->info.valid_hooks & (1 << i))
-                       h->cache_num_builtins++;
-       }
-
-       prev = NULL;
-       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
-                     add_chain, h, &prev);
-
-       qsort(h->cache_chain_heads + h->cache_num_builtins,
-             h->cache_num_chains - h->cache_num_builtins,
-             sizeof(struct chain_cache), alphasort);
-
-       return 1;
-}
-
-static int 
-correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
-{
-       int i;          /* needs to be signed because deleting first
-                          chain can make it drop to -1 */
-
-       if (!delta)
-               return 1;
-
-       for (i = 0; i < h->cache_num_chains; i++) {
-               struct chain_cache *cc = &h->cache_chain_heads[i];
-
-               if (delta < 0) {
-                       /* take care about deleted chains */
-                       if (cc->start_off > offset+delta
-                           && cc->end_off < offset) {
-                               /* this chain is within the deleted range,
-                                * let's remove it from the cache */
-                               void *start;
-                               unsigned int size;
-
-                               h->cache_num_chains--;
-
-                               /* no need for memmove since we are 
-                                * removing the last entry */
-                               if (i >= h->cache_num_chains)
-                                       continue;
-
-                               start = &h->cache_chain_heads[i+1];
-                               size = (h->cache_num_chains-i)
-                                       * sizeof(struct chain_cache);
-                               memmove(cc, start, size);
-
-                               /* iterate over same index again, since
-                                * it is now a different chain */
-                               i--;
-                               continue;
-                       }
-               }
-
-               if (cc->start_off > offset)
-                       cc->start_off += delta;
-
-               if (cc->end_off >= offset)
-                       cc->end_off += delta;
-       }
-       /* HW_FIXME: sorting might be needed, but just in case a new chain was
-        * added */
-
-       return 1;
-}
-
-static int
-add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
-               unsigned int end_off)
-{
-       struct chain_cache *ccs = realloc(h->cache_chain_heads, 
-                                         (h->new_number / 2 + 4 + 1)
-                                          * sizeof(struct chain_cache));
-       struct chain_cache *newcc;
-       
-       if (!ccs)
-               return 0;
-
-       h->cache_chain_heads = ccs;
-       newcc = &h->cache_chain_heads[h->cache_num_chains];
-       h->cache_num_chains++;
-
-       strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
-       newcc->start_off = start_off;
-       newcc->end_off = end_off;
-
-       return 1;
-}
-
-/* Returns cache ptr if found, otherwise NULL. */
-static struct chain_cache *
-find_label(const char *name, TC_HANDLE_T handle)
-{
-       unsigned int i;
-
-       if (handle->cache_chain_heads == NULL
-           && !populate_cache(handle))
-               return NULL;
-
-       /* FIXME: Linear search through builtins, then binary --RR */
-       for (i = 0; i < handle->cache_num_chains; i++) {
-               if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
-                       return &handle->cache_chain_heads[i];
-       }
-
-       return NULL;
+#endif
 }
 
 /* Does this chain exist? */
 int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
 {
-       return find_label(chain, handle) != NULL;
+       iptc_fn = TC_IS_CHAIN;
+       return iptcc_find_label(chain, handle) != NULL;
 }
 
-/* Returns the position of the final (ie. unconditional) element. */
-static unsigned int
-get_chain_end(const TC_HANDLE_T handle, unsigned int start)
+static void iptcc_chain_iterator_advance(TC_HANDLE_T handle)
 {
-       unsigned int last_off, off;
-       STRUCT_ENTRY *e;
-
-       last_off = start;
-       e = get_entry(handle, start);
-
-       /* Terminate when we meet a error label or a hook entry. */
-       for (off = start + e->next_offset;
-            off < handle->entries.size;
-            last_off = off, off += e->next_offset) {
-               STRUCT_ENTRY_TARGET *t;
-               unsigned int i;
-
-               e = get_entry(handle, off);
+       struct chain_head *c = handle->chain_iterator_cur;
 
-               /* We hit an entry point. */
-               for (i = 0; i < NUMHOOKS; i++) {
-                       if ((handle->info.valid_hooks & (1 << i))
-                           && off == handle->info.hook_entry[i])
-                               return last_off;
-               }
-
-               /* We hit a user chain label */
-               t = GET_TARGET(e);
-               if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
-                       return last_off;
-       }
-       /* SHOULD NEVER HAPPEN */
-       fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
-               handle->entries.size, off);
-       abort();
+       if (c->list.next == &handle->chains)
+               handle->chain_iterator_cur = NULL;
+       else
+               handle->chain_iterator_cur = 
+                       list_entry(c->list.next, struct chain_head, list);
 }
 
 /* Iterator functions to run through the chains. */
 const char *
 TC_FIRST_CHAIN(TC_HANDLE_T *handle)
 {
-       if ((*handle)->cache_chain_heads == NULL
-           && !populate_cache(*handle))
+       struct chain_head *c = list_entry((*handle)->chains.next,
+                                         struct chain_head, list);
+
+       iptc_fn = TC_FIRST_CHAIN;
+
+
+       if (list_empty(&(*handle)->chains)) {
+               DEBUGP(": no chains\n");
                return NULL;
+       }
 
-       (*handle)->cache_chain_iteration
-               = &(*handle)->cache_chain_heads[0];
+       (*handle)->chain_iterator_cur = c;
+       iptcc_chain_iterator_advance(*handle);
 
-       return (*handle)->cache_chain_iteration->name;
+       DEBUGP(": returning `%s'\n", c->name);
+       return c->name;
 }
 
 /* Iterator functions to run through the chains.  Returns NULL at end. */
 const char *
 TC_NEXT_CHAIN(TC_HANDLE_T *handle)
 {
-       (*handle)->cache_chain_iteration++;
+       struct chain_head *c = (*handle)->chain_iterator_cur;
+
+       iptc_fn = TC_NEXT_CHAIN;
 
-       if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
-           == (*handle)->cache_num_chains)
+       if (!c) {
+               DEBUGP(": no more chains\n");
                return NULL;
+       }
 
-       return (*handle)->cache_chain_iteration->name;
+       iptcc_chain_iterator_advance(*handle);
+       
+       DEBUGP(": returning `%s'\n", c->name);
+       return c->name;
 }
 
 /* Get first rule in the given chain: NULL for empty chain. */
 const STRUCT_ENTRY *
 TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
 {
-       struct chain_cache *c;
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_FIRST_RULE;
 
-       c = find_label(chain, *handle);
+       DEBUGP("first rule(%s): ", chain);
+
+       c = iptcc_find_label(chain, *handle);
        if (!c) {
                errno = ENOENT;
                return NULL;
        }
 
        /* Empty chain: single return/policy rule */
-       if (c->start_off == c->end_off)
+       if (list_empty(&c->rules)) {
+               DEBUGP_C("no rules, returning NULL\n");
                return NULL;
+       }
 
-       (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
-       return offset2entry(*handle, c->start_off);
+       r = list_entry(c->rules.next, struct rule_head, list);
+       (*handle)->rule_iterator_cur = r;
+       DEBUGP_C("%p\n", r);
+
+       return r->entry;
 }
 
 /* Returns NULL when rules run out. */
 const STRUCT_ENTRY *
 TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
 {
-       if ((void *)prev + prev->next_offset
-           == (void *)(*handle)->cache_rule_end)
+       struct rule_head *r;
+
+       iptc_fn = TC_NEXT_RULE;
+       DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur);
+
+       if (!(*handle)->rule_iterator_cur) {
+               DEBUGP_C("returning NULL\n");
                return NULL;
+       }
+       
+       r = list_entry((*handle)->rule_iterator_cur->list.next, 
+                       struct rule_head, list);
+
+       iptc_fn = TC_NEXT_RULE;
+
+       DEBUGP_C("next=%p, head=%p...", &r->list, 
+               &(*handle)->rule_iterator_cur->chain->rules);
+
+       if (&r->list == &(*handle)->rule_iterator_cur->chain->rules) {
+               (*handle)->rule_iterator_cur = NULL;
+               DEBUGP_C("finished, returning NULL\n");
+               return NULL;
+       }
+
+       (*handle)->rule_iterator_cur = r;
 
-       return (void *)prev + prev->next_offset;
+       /* NOTE: prev is without any influence ! */
+       DEBUGP_C("returning rule %p\n", r);
+       return r->entry;
 }
 
-#if 0
 /* How many rules in this chain? */
 unsigned int
 TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
 {
-       unsigned int off = 0;
-       STRUCT_ENTRY *start, *end;
-
+       struct chain_head *c;
+       iptc_fn = TC_NUM_RULES;
        CHECK(*handle);
-       if (!find_label(&off, chain, *handle)) {
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
                errno = ENOENT;
                return (unsigned int)-1;
        }
-
-       start = get_entry(*handle, off);
-       end = get_entry(*handle, get_chain_end(*handle, off));
-
-       return entry2index(*handle, end) - entry2index(*handle, start);
+       
+       return c->num_rules;
 }
 
-/* Get n'th rule in this chain. */
 const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
                                unsigned int n,
                                TC_HANDLE_T *handle)
 {
-       unsigned int pos = 0, chainindex;
+       struct chain_head *c;
+       struct rule_head *r;
+       
+       iptc_fn = TC_GET_RULE;
 
        CHECK(*handle);
-       if (!find_label(&pos, chain, *handle)) {
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
                errno = ENOENT;
                return NULL;
        }
 
-       chainindex = entry2index(*handle, get_entry(*handle, pos));
-
-       return index2entry(*handle, chainindex + n);
+       r = iptcc_get_rule_num(c, n);
+       if (!r)
+               return NULL;
+       return r->entry;
 }
-#endif
 
-static const char *
-target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+/* Returns a pointer to the target name of this position. */
+const char *standard_target_map(int verdict)
 {
-       int spos;
-       unsigned int labelidx;
-       STRUCT_ENTRY *jumpto;
-
-       /* To avoid const warnings */
-       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
-
-       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
-               return GET_TARGET(e)->u.user.name;
-
-       /* Standard target: evaluate */
-       spos = *(int *)GET_TARGET(e)->data;
-       if (spos < 0) {
-               if (spos == RETURN)
+       switch (verdict) {
+               case RETURN:
                        return LABEL_RETURN;
-               else if (spos == -NF_ACCEPT-1)
+                       break;
+               case -NF_ACCEPT-1:
                        return LABEL_ACCEPT;
-               else if (spos == -NF_DROP-1)
+                       break;
+               case -NF_DROP-1:
                        return LABEL_DROP;
-               else if (spos == -NF_QUEUE-1)
+                       break;
+               case -NF_QUEUE-1:
                        return LABEL_QUEUE;
-
-               fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
-                       entry2offset(handle, e), handle->entries.size,
-                       spos);
-               abort();
+                       break;
+               default:
+                       fprintf(stderr, "ERROR: %d not a valid target)\n",
+                               verdict);
+                       abort();
+                       break;
        }
-
-       jumpto = get_entry(handle, spos);
-
-       /* Fall through rule */
-       if (jumpto == (void *)e + e->next_offset)
-               return "";
-
-       /* Must point to head of a chain: ie. after error rule */
-       labelidx = entry2index(handle, jumpto) - 1;
-       return get_errorlabel(handle, index2offset(handle, labelidx));
+       /* not reached */
+       return NULL;
 }
 
 /* Returns a pointer to the target name of this position. */
-const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
                          TC_HANDLE_T *handle)
 {
-       return target_name(*handle, e);
-}
-
-/* Is this a built-in chain?  Actually returns hook + 1. */
-int
-TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
-{
-       unsigned int i;
-
-       for (i = 0; i < NUMHOOKS; i++) {
-               if ((handle->info.valid_hooks & (1 << i))
-                   && handle->hooknames[i]
-                   && strcmp(handle->hooknames[i], chain) == 0)
-                       return i+1;
-       }
-       return 0;
-}
-
-/* Get the policy of a given built-in chain */
-const char *
-TC_GET_POLICY(const char *chain,
-             STRUCT_COUNTERS *counters,
-             TC_HANDLE_T *handle)
-{
-       unsigned int start;
-       STRUCT_ENTRY *e;
-       int hook;
-
-       hook = TC_BUILTIN(chain, *handle);
-       if (hook != 0)
-               start = (*handle)->info.hook_entry[hook-1];
-       else
-               return NULL;
-
-       e = get_entry(*handle, get_chain_end(*handle, start));
-       *counters = e->counters;
-
-       return target_name(*handle, e);
-}
+       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+       struct rule_head *r = container_of(e, struct rule_head, entry[0]);
 
-static inline int
-correct_verdict(STRUCT_ENTRY *e,
-               char *base,
-               unsigned int offset, int delta_offset)
-{
-       STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
-       unsigned int curr = (char *)e - base;
+       iptc_fn = TC_GET_TARGET;
 
-       /* Trap: insert of fall-through rule.  Don't change fall-through
-          verdict to jump-over-next-rule. */
-       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
-           && t->verdict > (int)offset
-           && !(curr == offset &&
-                t->verdict == curr + e->next_offset)) {
-               t->verdict += delta_offset;
+       switch(r->type) {
+               int spos;
+               case IPTCC_R_FALLTHROUGH:
+                       return "";
+                       break;
+               case IPTCC_R_JUMP:
+                       DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
+                       return r->jump->name;
+                       break;
+               case IPTCC_R_STANDARD:
+                       spos = *(int *)GET_TARGET(e)->data;
+                       DEBUGP("r=%p, spos=%d'\n", r, spos);
+                       return standard_target_map(spos);
+                       break;
+               case IPTCC_R_MODULE:
+                       return GET_TARGET(e)->u.user.name;
+                       break;
        }
-
-       return 0;
-}
-
-/* Adjusts standard verdict jump positions after an insertion/deletion. */
-static int
-set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
-{
-       ENTRY_ITERATE((*handle)->entries.entrytable,
-                     (*handle)->entries.size,
-                     correct_verdict, (char *)(*handle)->entries.entrytable,
-                     offset, delta_offset);
-
-       set_changed(*handle);
-       return 1;
+       return NULL;
 }
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+       struct chain_head *c;
+       
+       iptc_fn = TC_BUILTIN;
 
-/* If prepend is set, then we are prepending to a chain: if the
- * insertion position is an entry point, keep the entry point. */
-static int
-insert_rules(unsigned int num_rules, unsigned int rules_size,
-            const STRUCT_ENTRY *insert,
-            unsigned int offset, unsigned int num_rules_offset,
-            int prepend,
-            TC_HANDLE_T *handle)
-{
-       TC_HANDLE_T newh;
-       STRUCT_GETINFO newinfo;
-       unsigned int i;
-
-       if (offset >= (*handle)->entries.size) {
-               errno = EINVAL;
-               return 0;
-       }
-
-       newinfo = (*handle)->info;
-
-       /* Fix up entry points. */
-       for (i = 0; i < NUMHOOKS; i++) {
-               /* Entry points to START of chain, so keep same if
-                   inserting on at that point. */
-               if ((*handle)->info.hook_entry[i] > offset)
-                       newinfo.hook_entry[i] += rules_size;
-
-               /* Underflow always points to END of chain (policy),
-                  so if something is inserted at same point, it
-                  should be advanced. */
-               if ((*handle)->info.underflow[i] >= offset)
-                       newinfo.underflow[i] += rules_size;
-       }
-
-       newh = alloc_handle((*handle)->info.name,
-                           (*handle)->entries.size + rules_size,
-                           (*handle)->new_number + num_rules);
-       if (!newh)
-               return 0;
-       newh->info = newinfo;
-
-       /* Copy pre... */
-       memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
-       /* ... Insert new ... */
-       memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
-       /* ... copy post */
-       memcpy((char *)newh->entries.entrytable + offset + rules_size,
-              (char *)(*handle)->entries.entrytable + offset,
-              (*handle)->entries.size - offset);
-
-       /* Move counter map. */
-       /* Copy pre... */
-       memcpy(newh->counter_map, (*handle)->counter_map,
-              sizeof(struct counter_map) * num_rules_offset);
-       /* ... copy post */
-       memcpy(newh->counter_map + num_rules_offset + num_rules,
-              (*handle)->counter_map + num_rules_offset,
-              sizeof(struct counter_map) * ((*handle)->new_number
-                                            - num_rules_offset));
-       /* Set intermediates to no counter copy */
-       for (i = 0; i < num_rules; i++)
-               newh->counter_map[num_rules_offset+i]
-                       = ((struct counter_map){ COUNTER_MAP_SET, 0 });
-
-       newh->new_number = (*handle)->new_number + num_rules;
-       newh->entries.size = (*handle)->entries.size + rules_size;
-       newh->hooknames = (*handle)->hooknames;
-
-       newh->cache_chain_heads = (*handle)->cache_chain_heads;
-       newh->cache_num_builtins = (*handle)->cache_num_builtins;
-       newh->cache_num_chains = (*handle)->cache_num_chains;
-       newh->cache_rule_end = (*handle)->cache_rule_end;
-       newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
-       if (!correct_cache(newh, offset, rules_size)) {
-               free(newh);
+       c = iptcc_find_label(chain, handle);
+       if (!c) {
+               errno = ENOENT;
                return 0;
        }
 
-       free(*handle);
-       *handle = newh;
-
-       return set_verdict(offset, rules_size, handle);
+       return iptcc_is_builtin(c);
 }
 
-static int
-delete_rules(unsigned int num_rules, unsigned int rules_size,
-            unsigned int offset, unsigned int num_rules_offset,
-            TC_HANDLE_T *handle)
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
 {
-       unsigned int i;
+       struct chain_head *c;
 
-       if (offset + rules_size > (*handle)->entries.size) {
-               errno = EINVAL;
-               return 0;
-       }
+       iptc_fn = TC_GET_POLICY;
 
-       /* Fix up entry points. */
-       for (i = 0; i < NUMHOOKS; i++) {
-               /* In practice, we never delete up to a hook entry,
-                  since the built-in chains are always first,
-                  so these two are never equal */
-               if ((*handle)->info.hook_entry[i] >= offset + rules_size)
-                       (*handle)->info.hook_entry[i] -= rules_size;
-               else if ((*handle)->info.hook_entry[i] > offset) {
-                       fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
-                               i, (*handle)->info.hook_entry[i], offset);
-                       abort();
-               }
+       DEBUGP("called for chain %s\n", chain);
 
-               /* Underflow points to policy (terminal) rule in
-                   built-in, so sequality is valid here (when deleting
-                   the last rule). */
-               if ((*handle)->info.underflow[i] >= offset + rules_size)
-                       (*handle)->info.underflow[i] -= rules_size;
-               else if ((*handle)->info.underflow[i] > offset) {
-                       fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
-                               i, (*handle)->info.underflow[i], offset);
-                       abort();
-               }
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
        }
 
-       /* Move the rules down. */
-       memmove((char *)(*handle)->entries.entrytable + offset,
-               (char *)(*handle)->entries.entrytable + offset + rules_size,
-               (*handle)->entries.size - (offset + rules_size));
-
-       /* Move the counter map down. */
-       memmove(&(*handle)->counter_map[num_rules_offset],
-               &(*handle)->counter_map[num_rules_offset + num_rules],
-               sizeof(struct counter_map)
-               * ((*handle)->new_number - (num_rules + num_rules_offset)));
-
-       /* Fix numbers */
-       (*handle)->new_number -= num_rules;
-       (*handle)->entries.size -= rules_size;
+       if (!iptcc_is_builtin(c))
+               return NULL;
 
-       /* Fix the chain cache */
-       if (!correct_cache(*handle, offset+rules_size, -(int)rules_size))
-               return 0;
+       *counters = c->counters;
 
-       return set_verdict(offset, -(int)rules_size, handle);
+       return standard_target_map(c->verdict);
 }
 
 static int
-standard_map(STRUCT_ENTRY *e, int verdict)
+iptcc_standard_map(struct rule_head *r, int verdict)
 {
+       STRUCT_ENTRY *e = r->entry;
        STRUCT_STANDARD_TARGET *t;
 
        t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
@@ -970,64 +1217,62 @@ standard_map(STRUCT_ENTRY *e, int verdict)
        strcpy(t->target.u.user.name, STANDARD_TARGET);
        t->verdict = verdict;
 
+       r->type = IPTCC_R_STANDARD;
+
        return 1;
 }
 
 static int
-map_target(const TC_HANDLE_T handle,
-          STRUCT_ENTRY *e,
-          unsigned int offset,
-          STRUCT_ENTRY_TARGET *old)
+iptcc_map_target(const TC_HANDLE_T handle,
+          struct rule_head *r)
 {
+       STRUCT_ENTRY *e = r->entry;
        STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
 
-       /* Save old target (except data, which we don't change, except for
-          standard case, where we don't care). */
-       *old = *t;
-
        /* Maybe it's empty (=> fall through) */
-       if (strcmp(t->u.user.name, "") == 0)
-               return standard_map(e, offset + e->next_offset);
+       if (strcmp(t->u.user.name, "") == 0) {
+               r->type = IPTCC_R_FALLTHROUGH;
+               return 1;
+       }
        /* Maybe it's a standard target name... */
        else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
-               return standard_map(e, -NF_ACCEPT - 1);
+               return iptcc_standard_map(r, -NF_ACCEPT - 1);
        else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
-               return standard_map(e, -NF_DROP - 1);
+               return iptcc_standard_map(r, -NF_DROP - 1);
        else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
-               return standard_map(e, -NF_QUEUE - 1);
+               return iptcc_standard_map(r, -NF_QUEUE - 1);
        else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
-               return standard_map(e, RETURN);
+               return iptcc_standard_map(r, RETURN);
        else if (TC_BUILTIN(t->u.user.name, handle)) {
                /* Can't jump to builtins. */
                errno = EINVAL;
                return 0;
        } else {
                /* Maybe it's an existing chain name. */
-               struct chain_cache *c;
-
-               c = find_label(t->u.user.name, handle);
-               if (c)
-                       return standard_map(e, c->start_off);
+               struct chain_head *c;
+               DEBUGP("trying to find chain `%s': ", t->u.user.name);
+
+               c = iptcc_find_label(t->u.user.name, handle);
+               if (c) {
+                       DEBUGP_C("found!\n");
+                       r->type = IPTCC_R_JUMP;
+                       r->jump = c;
+                       c->references++;
+                       return 1;
+               }
+               DEBUGP_C("not found :(\n");
        }
 
        /* Must be a module?  If not, kernel will reject... */
-       /* memset to all 0 for your memcmp convenience. */
+       /* memset to all 0 for your memcmp convenience: don't clear version */
        memset(t->u.user.name + strlen(t->u.user.name),
               0,
-              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+              FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name));
+       r->type = IPTCC_R_MODULE;
+       set_changed(handle);
        return 1;
 }
 
-static void
-unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
-{
-       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
-
-       /* Save old target (except data, which we don't change, except for
-          standard case, where we don't care). */
-       *t = *old;
-}
-
 /* Insert the entry `fw' in chain `chain' into position `rulenum'. */
 int
 TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
@@ -1035,36 +1280,56 @@ TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
                unsigned int rulenum,
                TC_HANDLE_T *handle)
 {
-       unsigned int chainindex, offset;
-       STRUCT_ENTRY_TARGET old;
-       struct chain_cache *c;
-       STRUCT_ENTRY *tmp;
-       int ret;
+       struct chain_head *c;
+       struct rule_head *r;
+       struct list_head *prev;
 
        iptc_fn = TC_INSERT_ENTRY;
-       if (!(c = find_label(chain, *handle))) {
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-
-       tmp = index2entry(*handle, chainindex + rulenum);
-       if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
+       /* first rulenum index = 0
+          first c->num_rules index = 1 */
+       if (rulenum > c->num_rules) {
                errno = E2BIG;
                return 0;
        }
-       offset = index2offset(*handle, chainindex + rulenum);
 
-       /* Mapping target actually alters entry, but that's
-           transparent to the caller. */
-       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+       /* If we are inserting at the end just take advantage of the
+          double linked list, insert will happen before the entry
+          prev points to. */
+       if (rulenum == c->num_rules) {
+               prev = &c->rules;
+       } else if (rulenum + 1 <= c->num_rules/2) {
+               r = iptcc_get_rule_num(c, rulenum + 1);
+               prev = &r->list;
+       } else {
+               r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+               prev = &r->list;
+       }
+
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       if (!iptcc_map_target(*handle, r)) {
+               free(r);
                return 0;
+       }
+
+       list_add_tail(&r->list, prev);
+       c->num_rules++;
+
+       set_changed(*handle);
 
-       ret = insert_rules(1, e->next_offset, e, offset,
-                          chainindex + rulenum, rulenum == 0, handle);
-       unmap_target((STRUCT_ENTRY *)e, &old);
-       return ret;
+       return 1;
 }
 
 /* Atomically replace rule `rulenum' in `chain' with `fw'. */
@@ -1074,40 +1339,47 @@ TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
                 unsigned int rulenum,
                 TC_HANDLE_T *handle)
 {
-       unsigned int chainindex, offset;
-       STRUCT_ENTRY_TARGET old;
-       struct chain_cache *c;
-       STRUCT_ENTRY *tmp;
-       int ret;
+       struct chain_head *c;
+       struct rule_head *r, *old;
 
        iptc_fn = TC_REPLACE_ENTRY;
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-
-       tmp = index2entry(*handle, chainindex + rulenum);
-       if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
+       if (rulenum >= c->num_rules) {
                errno = E2BIG;
                return 0;
        }
 
-       offset = index2offset(*handle, chainindex + rulenum);
-       /* Replace = delete and insert. */
-       if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
-                         offset, chainindex + rulenum, handle))
+       /* Take advantage of the double linked list if possible. */
+       if (rulenum + 1 <= c->num_rules/2) {
+               old = iptcc_get_rule_num(c, rulenum + 1);
+       } else {
+               old = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+       }
+
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               errno = ENOMEM;
                return 0;
+       }
 
-       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       if (!iptcc_map_target(*handle, r)) {
+               free(r);
                return 0;
+       }
+
+       list_add(&r->list, &old->list);
+       iptcc_delete_rule(old);
+
+       set_changed(*handle);
 
-       ret = insert_rules(1, e->next_offset, e, offset,
-                          chainindex + rulenum, 1, handle);
-       unmap_target((STRUCT_ENTRY *)e, &old);
-       return ret;
+       return 1;
 }
 
 /* Append entry `fw' to chain `chain'.  Equivalent to insert with
@@ -1117,24 +1389,37 @@ TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
                const STRUCT_ENTRY *e,
                TC_HANDLE_T *handle)
 {
-       struct chain_cache *c;
-       STRUCT_ENTRY_TARGET old;
-       int ret;
+       struct chain_head *c;
+       struct rule_head *r;
 
        iptc_fn = TC_APPEND_ENTRY;
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("unable to find chain `%s'\n", chain);
                errno = ENOENT;
                return 0;
        }
 
-       if (!map_target(*handle, (STRUCT_ENTRY *)e,
-                       c->end_off, &old))
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               DEBUGP("unable to allocate rule for chain `%s'\n", chain);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       if (!iptcc_map_target(*handle, r)) {
+               DEBUGP("unable to map target of rule for chain `%s'\n", chain);
+               free(r);
                return 0;
+       }
+
+       list_add_tail(&r->list, &c->rules);
+       c->num_rules++;
+
+       set_changed(*handle);
 
-       ret = insert_rules(1, e->next_offset, e, c->end_off, 
-                          offset2index(*handle, c->end_off), 0, handle);
-       unmap_target((STRUCT_ENTRY *)e, &old);
-       return ret;
+       return 1;
 }
 
 static inline int
@@ -1165,20 +1450,42 @@ match_different(const STRUCT_ENTRY_MATCH *a,
 }
 
 static inline int
-target_different(const unsigned char *a_targdata,
-                const unsigned char *b_targdata,
-                unsigned int tdatasize,
-                const unsigned char *mask)
+target_same(struct rule_head *a, struct rule_head *b,const unsigned char *mask)
 {
        unsigned int i;
-       for (i = 0; i < tdatasize; i++)
-               if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
-                       return 1;
+       STRUCT_ENTRY_TARGET *ta, *tb;
 
-       return 0;
+       if (a->type != b->type)
+               return 0;
+
+       ta = GET_TARGET(a->entry);
+       tb = GET_TARGET(b->entry);
+
+       switch (a->type) {
+       case IPTCC_R_FALLTHROUGH:
+               return 1;
+       case IPTCC_R_JUMP:
+               return a->jump == b->jump;
+       case IPTCC_R_STANDARD:
+               return ((STRUCT_STANDARD_TARGET *)ta)->verdict
+                       == ((STRUCT_STANDARD_TARGET *)tb)->verdict;
+       case IPTCC_R_MODULE:
+               if (ta->u.target_size != tb->u.target_size)
+                       return 0;
+               if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+                       return 0;
+
+               for (i = 0; i < ta->u.target_size - sizeof(*ta); i++)
+                       if (((ta->data[i] ^ tb->data[i]) & mask[i]) != 0)
+                               return 0;
+               return 1;
+       default:
+               fprintf(stderr, "ERROR: bad type %i\n", a->type);
+               abort();
+       }
 }
 
-static int
+static unsigned char *
 is_same(const STRUCT_ENTRY *a,
        const STRUCT_ENTRY *b,
        unsigned char *matchmask);
@@ -1190,87 +1497,106 @@ TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
                unsigned char *matchmask,
                TC_HANDLE_T *handle)
 {
-       unsigned int offset;
-       struct chain_cache *c;
-       STRUCT_ENTRY *e, *fw;
+       struct chain_head *c;
+       struct rule_head *r, *i;
 
        iptc_fn = TC_DELETE_ENTRY;
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       fw = malloc(origfw->next_offset);
-       if (fw == NULL) {
+       /* Create a rule_head from origfw. */
+       r = iptcc_alloc_rule(c, origfw->next_offset);
+       if (!r) {
                errno = ENOMEM;
                return 0;
        }
 
-       for (offset = c->start_off; offset < c->end_off;
-            offset += e->next_offset) {
-               STRUCT_ENTRY_TARGET discard;
+       memcpy(r->entry, origfw, origfw->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_NOMAP;
+       if (!iptcc_map_target(*handle, r)) {
+               DEBUGP("unable to map target of rule for chain `%s'\n", chain);
+               free(r);
+               return 0;
+       }
 
-               memcpy(fw, origfw, origfw->next_offset);
+       list_for_each_entry(i, &c->rules, list) {
+               unsigned char *mask;
 
-               /* FIXME: handle this in is_same --RR */
-               if (!map_target(*handle, fw, offset, &discard)) {
-                       free(fw);
-                       return 0;
-               }
-               e = get_entry(*handle, offset);
+               mask = is_same(r->entry, i->entry, matchmask);
+               if (!mask)
+                       continue;
 
-#if 0
-               printf("Deleting:\n");
-               dump_entry(newe);
-#endif
-               if (is_same(e, fw, matchmask)) {
-                       int ret;
-                       ret = delete_rules(1, e->next_offset,
-                                          offset, entry2index(*handle, e),
-                                          handle);
-                       free(fw);
-                       return ret;
+               if (!target_same(r, i, mask))
+                       continue;
+
+               /* If we are about to delete the rule that is the
+                * current iterator, move rule iterator back.  next
+                * pointer will then point to real next node */
+               if (i == (*handle)->rule_iterator_cur) {
+                       (*handle)->rule_iterator_cur = 
+                               list_entry((*handle)->rule_iterator_cur->list.prev,
+                                          struct rule_head, list);
                }
+
+               c->num_rules--;
+               iptcc_delete_rule(i);
+
+               set_changed(*handle);
+               free(r);
+               return 1;
        }
 
-       free(fw);
+       free(r);
        errno = ENOENT;
        return 0;
 }
 
+
 /* Delete the rule in position `rulenum' in `chain'. */
 int
 TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
                    unsigned int rulenum,
                    TC_HANDLE_T *handle)
 {
-       unsigned int index;
-       int ret;
-       STRUCT_ENTRY *e;
-       struct chain_cache *c;
+       struct chain_head *c;
+       struct rule_head *r;
 
        iptc_fn = TC_DELETE_NUM_ENTRY;
-       if (!(c = find_label(chain, *handle))) {
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       index = offset2index(*handle, c->start_off) + rulenum;
-
-       if (index >= offset2index(*handle, c->end_off)) {
+       if (rulenum >= c->num_rules) {
                errno = E2BIG;
                return 0;
        }
 
-       e = index2entry(*handle, index);
-       if (e == NULL) {
-               errno = EINVAL;
-               return 0;
+       /* Take advantage of the double linked list if possible. */
+       if (rulenum + 1 <= c->num_rules/2) {
+               r = iptcc_get_rule_num(c, rulenum + 1);
+       } else {
+               r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+       }
+
+       /* If we are about to delete the rule that is the current
+        * iterator, move rule iterator back.  next pointer will then
+        * point to real next node */
+       if (r == (*handle)->rule_iterator_cur) {
+               (*handle)->rule_iterator_cur = 
+                       list_entry((*handle)->rule_iterator_cur->list.prev,
+                                  struct rule_head, list);
        }
 
-       ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
-                          index, handle);
-       return ret;
+       c->num_rules--;
+       iptcc_delete_rule(r);
+
+       set_changed(*handle);
+
+       return 1;
 }
 
 /* Check the packet `fw' on chain `chain'.  Returns the verdict, or
@@ -1280,6 +1606,7 @@ TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
                STRUCT_ENTRY *entry,
                TC_HANDLE_T *handle)
 {
+       iptc_fn = TC_CHECK_PACKET;
        errno = ENOSYS;
        return NULL;
 }
@@ -1288,47 +1615,44 @@ TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
 int
 TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
-       unsigned int startindex, endindex;
-       STRUCT_ENTRY *startentry, *endentry;
-       struct chain_cache *c;
-       int ret;
+       struct chain_head *c;
+       struct rule_head *r, *tmp;
 
        iptc_fn = TC_FLUSH_ENTRIES;
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
-       startindex = offset2index(*handle, c->start_off);
-       endindex = offset2index(*handle, c->end_off);
-       startentry = offset2entry(*handle, c->start_off);
-       endentry = offset2entry(*handle, c->end_off);
 
-       ret = delete_rules(endindex - startindex,
-                          (char *)endentry - (char *)startentry,
-                          c->start_off, startindex,
-                          handle);
-       return ret;
+       list_for_each_entry_safe(r, tmp, &c->rules, list) {
+               iptcc_delete_rule(r);
+       }
+
+       c->num_rules = 0;
+
+       set_changed(*handle);
+
+       return 1;
 }
 
 /* Zeroes the counters in a chain. */
 int
 TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
-       unsigned int i, end;
-       struct chain_cache *c;
+       struct chain_head *c;
+       struct rule_head *r;
 
-       if (!(c = find_label(chain, *handle))) {
+       iptc_fn = TC_ZERO_ENTRIES;
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       i = offset2index(*handle, c->start_off);
-       end = offset2index(*handle, c->end_off);
-
-       for (; i <= end; i++) {
-               if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
-                       (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
+       list_for_each_entry(r, &c->rules, list) {
+               if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+                       r->counter_map.maptype = COUNTER_MAP_ZEROED;
        }
+
        set_changed(*handle);
 
        return 1;
@@ -1339,29 +1663,23 @@ TC_READ_COUNTER(const IPT_CHAINLABEL chain,
                unsigned int rulenum,
                TC_HANDLE_T *handle)
 {
-       STRUCT_ENTRY *e;
-       struct chain_cache *c;
-       unsigned int chainindex, end;
+       struct chain_head *c;
+       struct rule_head *r;
 
        iptc_fn = TC_READ_COUNTER;
        CHECK(*handle);
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return NULL;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-       end = offset2index(*handle, c->end_off);
-
-       if (chainindex + rulenum > end) {
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
                errno = E2BIG;
                return NULL;
        }
 
-       e = index2entry(*handle, chainindex + rulenum);
-
-       return &e->counters;
+       return &r->entry[0].counters;
 }
 
 int
@@ -1369,33 +1687,24 @@ TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
                unsigned int rulenum,
                TC_HANDLE_T *handle)
 {
-       STRUCT_ENTRY *e;
-       struct chain_cache *c;
-       unsigned int chainindex, end;
+       struct chain_head *c;
+       struct rule_head *r;
        
        iptc_fn = TC_ZERO_COUNTER;
        CHECK(*handle);
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-       end = offset2index(*handle, c->end_off);
-
-       if (chainindex + rulenum > end) {
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
                errno = E2BIG;
                return 0;
        }
 
-       e = index2entry(*handle, chainindex + rulenum);
-
-       if ((*handle)->counter_map[chainindex + rulenum].maptype
-                       == COUNTER_MAP_NORMAL_MAP) {
-               (*handle)->counter_map[chainindex + rulenum].maptype
-                        = COUNTER_MAP_ZEROED;
-       }
+       if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+               r->counter_map.maptype = COUNTER_MAP_ZEROED;
 
        set_changed(*handle);
 
@@ -1408,30 +1717,25 @@ TC_SET_COUNTER(const IPT_CHAINLABEL chain,
               STRUCT_COUNTERS *counters,
               TC_HANDLE_T *handle)
 {
+       struct chain_head *c;
+       struct rule_head *r;
        STRUCT_ENTRY *e;
-       struct chain_cache *c;
-       unsigned int chainindex, end;
 
        iptc_fn = TC_SET_COUNTER;
        CHECK(*handle);
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-       end = offset2index(*handle, c->end_off);
-
-       if (chainindex + rulenum > end) {
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
                errno = E2BIG;
                return 0;
        }
 
-       e = index2entry(*handle, chainindex + rulenum);
-
-       (*handle)->counter_map[chainindex + rulenum].maptype
-               = COUNTER_MAP_SET;
+       e = r->entry;
+       r->counter_map.maptype = COUNTER_MAP_SET;
 
        memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
 
@@ -1446,82 +1750,42 @@ TC_SET_COUNTER(const IPT_CHAINLABEL chain,
 int
 TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
-       int ret;
-       struct {
-               STRUCT_ENTRY head;
-               struct ipt_error_target name;
-               STRUCT_ENTRY ret;
-               STRUCT_STANDARD_TARGET target;
-       } newc;
-       unsigned int destination;
+       static struct chain_head *c;
 
        iptc_fn = TC_CREATE_CHAIN;
 
        /* find_label doesn't cover built-in targets: DROP, ACCEPT,
            QUEUE, RETURN. */
-       if (find_label(chain, *handle)
+       if (iptcc_find_label(chain, *handle)
            || strcmp(chain, LABEL_DROP) == 0
            || strcmp(chain, LABEL_ACCEPT) == 0
            || strcmp(chain, LABEL_QUEUE) == 0
            || strcmp(chain, LABEL_RETURN) == 0) {
+               DEBUGP("Chain `%s' already exists\n", chain);
                errno = EEXIST;
                return 0;
        }
 
        if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               DEBUGP("Chain name `%s' too long\n", chain);
                errno = EINVAL;
                return 0;
        }
 
-       memset(&newc, 0, sizeof(newc));
-       newc.head.target_offset = sizeof(STRUCT_ENTRY);
-       newc.head.next_offset
-               = sizeof(STRUCT_ENTRY)
-               + ALIGN(sizeof(struct ipt_error_target));
-       strcpy(newc.name.t.u.user.name, ERROR_TARGET);
-       newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
-       strcpy(newc.name.error, chain);
-
-       newc.ret.target_offset = sizeof(STRUCT_ENTRY);
-       newc.ret.next_offset
-               = sizeof(STRUCT_ENTRY)
-               + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
-       strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
-       newc.target.target.u.target_size
-               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
-       newc.target.verdict = RETURN;
-
-       destination = index2offset(*handle, (*handle)->new_number -1);
-
-       /* Add just before terminal entry */
-       ret = insert_rules(2, sizeof(newc), &newc.head,
-                          destination,
-                          (*handle)->new_number - 1,
-                          0, handle);
-
-       set_changed(*handle);
-
-       /* add chain cache info for this chain */
-       add_chain_cache(*handle, chain, 
-                       destination+newc.head.next_offset, 
-                       destination+newc.head.next_offset);
-
-       return ret;
-}
+       c = iptcc_alloc_chain_head(chain, 0);
+       if (!c) {
+               DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
+               errno = ENOMEM;
+               return 0;
 
-static int
-count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
-{
-       STRUCT_STANDARD_TARGET *t;
+       }
 
-       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
-               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+       DEBUGP("Creating chain `%s'\n", chain);
+       list_add_tail(&c->list, &(*handle)->chains);
 
-               if (t->verdict == offset)
-                       (*ref)++;
-       }
+       set_changed(*handle);
 
-       return 0;
+       return 1;
 }
 
 /* Get the number of references to this chain. */
@@ -1529,17 +1793,16 @@ int
 TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
                  TC_HANDLE_T *handle)
 {
-       struct chain_cache *c;
+       struct chain_head *c;
 
-       if (!(c = find_label(chain, *handle))) {
+       iptc_fn = TC_GET_REFERENCES;
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       *ref = 0;
-       ENTRY_ITERATE((*handle)->entries.entrytable,
-                     (*handle)->entries.size,
-                     count_ref, c->start_off, ref);
+       *ref = c->references;
+
        return 1;
 }
 
@@ -1547,48 +1810,53 @@ TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
 int
 TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
-       unsigned int labelidx, labeloff;
        unsigned int references;
-       struct chain_cache *c;
-       int ret;
-       STRUCT_ENTRY *start;
-
-       if (!TC_GET_REFERENCES(&references, chain, handle))
-               return 0;
+       struct chain_head *c;
 
        iptc_fn = TC_DELETE_CHAIN;
 
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("cannot find chain `%s'\n", chain);
+               errno = ENOENT;
+               return 0;
+       }
+
        if (TC_BUILTIN(chain, *handle)) {
+               DEBUGP("cannot remove builtin chain `%s'\n", chain);
                errno = EINVAL;
                return 0;
        }
 
-       if (references > 0) {
-               errno = EMLINK;
+       if (!TC_GET_REFERENCES(&references, chain, handle)) {
+               DEBUGP("cannot get references on chain `%s'\n", chain);
                return 0;
        }
 
-       if (!(c = find_label(chain, *handle))) {
-               errno = ENOENT;
+       if (references > 0) {
+               DEBUGP("chain `%s' still has references\n", chain);
+               errno = EMLINK;
                return 0;
        }
 
-       if (c->start_off != c->end_off) {
+       if (c->num_rules) {
+               DEBUGP("chain `%s' is not empty\n", chain);
                errno = ENOTEMPTY;
                return 0;
        }
 
-       /* Need label index: preceeds chain start */
-       labelidx = offset2index(*handle, c->start_off) - 1;
-       labeloff = index2offset(*handle, labelidx);
+       /* If we are about to delete the chain that is the current
+        * iterator, move chain iterator firward. */
+       if (c == (*handle)->chain_iterator_cur)
+               iptcc_chain_iterator_advance(*handle);
+
+       list_del(&c->list);
+       free(c);
 
-       start = offset2entry(*handle, c->start_off);
+       DEBUGP("chain `%s' deleted\n", chain);
 
-       ret = delete_rules(2,
-                          get_entry(*handle, labeloff)->next_offset
-                          + start->next_offset,
-                          labeloff, labelidx, handle);
-       return ret;
+       set_changed(*handle);
+
+       return 1;
 }
 
 /* Renames a chain. */
@@ -1596,15 +1864,12 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
                    const IPT_CHAINLABEL newname,
                    TC_HANDLE_T *handle)
 {
-       unsigned int labeloff, labelidx;
-       struct chain_cache *c;
-       struct ipt_error_target *t;
-
+       struct chain_head *c;
        iptc_fn = TC_RENAME_CHAIN;
 
        /* find_label doesn't cover built-in targets: DROP, ACCEPT,
            QUEUE, RETURN. */
-       if (find_label(newname, *handle)
+       if (iptcc_find_label(newname, *handle)
            || strcmp(newname, LABEL_DROP) == 0
            || strcmp(newname, LABEL_ACCEPT) == 0
            || strcmp(newname, LABEL_QUEUE) == 0
@@ -1613,7 +1878,7 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
                return 0;
        }
 
-       if (!(c = find_label(oldname, *handle))
+       if (!(c = iptcc_find_label(oldname, *handle))
            || TC_BUILTIN(oldname, *handle)) {
                errno = ENOENT;
                return 0;
@@ -1624,20 +1889,8 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
                return 0;
        }
 
-       /* Need label index: preceeds chain start */
-       labelidx = offset2index(*handle, c->start_off) - 1;
-       labeloff = index2offset(*handle, labelidx);
-
-       t = (struct ipt_error_target *)
-               GET_TARGET(get_entry(*handle, labeloff));
-
-       memset(t->error, 0, sizeof(t->error));
-       strcpy(t->error, newname);
-
-       /* update chain cache */
-       memset(c->name, 0, sizeof(c->name));
-       strcpy(c->name, newname);
-
+       strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
+       
        set_changed(*handle);
 
        return 1;
@@ -1650,51 +1903,37 @@ TC_SET_POLICY(const IPT_CHAINLABEL chain,
              STRUCT_COUNTERS *counters,
              TC_HANDLE_T *handle)
 {
-       unsigned int hook;
-       unsigned int policyoff, ctrindex;
-       STRUCT_ENTRY *e;
-       STRUCT_STANDARD_TARGET *t;
+       struct chain_head *c;
 
        iptc_fn = TC_SET_POLICY;
-       /* Figure out which chain. */
-       hook = TC_BUILTIN(chain, *handle);
-       if (hook == 0) {
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("cannot find chain `%s'\n", chain);
                errno = ENOENT;
                return 0;
-       } else
-               hook--;
+       }
 
-       policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
-       if (policyoff != (*handle)->info.underflow[hook]) {
-               printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
-                      chain, policyoff, (*handle)->info.underflow[hook]);
+       if (!iptcc_is_builtin(c)) {
+               DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
+               errno = ENOENT;
                return 0;
        }
 
-       e = get_entry(*handle, policyoff);
-       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
-
        if (strcmp(policy, LABEL_ACCEPT) == 0)
-               t->verdict = -NF_ACCEPT - 1;
+               c->verdict = -NF_ACCEPT - 1;
        else if (strcmp(policy, LABEL_DROP) == 0)
-               t->verdict = -NF_DROP - 1;
+               c->verdict = -NF_DROP - 1;
        else {
                errno = EINVAL;
                return 0;
        }
 
-       ctrindex = entry2index(*handle, e);
-
        if (counters) {
                /* set byte and packet counters */
-               memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
-
-               (*handle)->counter_map[ctrindex].maptype
-                       = COUNTER_MAP_SET;
-
+               memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
+               c->counter_map.maptype = COUNTER_MAP_SET;
        } else {
-               (*handle)->counter_map[ctrindex]
-                       = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+               c->counter_map.maptype = COUNTER_MAP_NOMAP;
        }
 
        set_changed(*handle);
@@ -1717,33 +1956,100 @@ subtract_counters(STRUCT_COUNTERS *answer,
        answer->bcnt = a->bcnt - b->bcnt;
 }
 
+
+static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters,
+                          unsigned int index)
+{
+       newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0});
+       DEBUGP_C("NOMAP => zero\n");
+}
+
+static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
+                               STRUCT_REPLACE *repl,
+                               unsigned int index,
+                               unsigned int mappos)
+{
+       /* Original read: X.
+        * Atomic read on replacement: X + Y.
+        * Currently in kernel: Z.
+        * Want in kernel: X + Y + Z.
+        * => Add in X + Y
+        * => Add in replacement read.
+        */
+       newcounters->counters[index] = repl->counters[mappos];
+       DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
+}
+
+static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
+                               STRUCT_REPLACE *repl,
+                               unsigned int index,
+                               unsigned int mappos,
+                               STRUCT_COUNTERS *counters)
+{
+       /* Original read: X.
+        * Atomic read on replacement: X + Y.
+        * Currently in kernel: Z.
+        * Want in kernel: Y + Z.
+        * => Add in Y.
+        * => Add in (replacement read - original read).
+        */
+       subtract_counters(&newcounters->counters[index],
+                         &repl->counters[mappos],
+                         counters);
+       DEBUGP_C("ZEROED => mappos %u\n", mappos);
+}
+
+static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
+                            unsigned int index,
+                            STRUCT_COUNTERS *counters)
+{
+       /* Want to set counter (iptables-restore) */
+
+       memcpy(&newcounters->counters[index], counters,
+               sizeof(STRUCT_COUNTERS));
+
+       DEBUGP_C("SET\n");
+}
+
+
 int
 TC_COMMIT(TC_HANDLE_T *handle)
 {
        /* Replace, then map back the counters. */
        STRUCT_REPLACE *repl;
        STRUCT_COUNTERS_INFO *newcounters;
-       unsigned int i;
+       struct chain_head *c;
+       int ret;
        size_t counterlen;
+       int new_number;
+       unsigned int new_size;
 
+       iptc_fn = TC_COMMIT;
        CHECK(*handle);
 
-       counterlen = sizeof(STRUCT_COUNTERS_INFO)
-                       + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
-
-#if 0
-       TC_DUMP_ENTRIES(*handle);
-#endif
-
        /* Don't commit if nothing changed. */
        if (!(*handle)->changed)
                goto finished;
 
-       repl = malloc(sizeof(*repl) + (*handle)->entries.size);
+       new_number = iptcc_compile_table_prep(*handle, &new_size);
+       if (new_number < 0) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       repl = malloc(sizeof(*repl) + new_size);
        if (!repl) {
                errno = ENOMEM;
                return 0;
        }
+       memset(repl, 0, sizeof(*repl) + new_size);
+
+#if 0
+       TC_DUMP_ENTRIES(*handle);
+#endif
+
+       counterlen = sizeof(STRUCT_COUNTERS_INFO)
+                       + sizeof(STRUCT_COUNTERS) * new_number;
 
        /* These are the old counters we will get from kernel */
        repl->counters = malloc(sizeof(STRUCT_COUNTERS)
@@ -1753,7 +2059,6 @@ TC_COMMIT(TC_HANDLE_T *handle)
                errno = ENOMEM;
                return 0;
        }
-
        /* These are the counters we're going to put back, later. */
        newcounters = malloc(counterlen);
        if (!newcounters) {
@@ -1762,21 +2067,40 @@ TC_COMMIT(TC_HANDLE_T *handle)
                errno = ENOMEM;
                return 0;
        }
+       memset(newcounters, 0, counterlen);
 
        strcpy(repl->name, (*handle)->info.name);
-       repl->num_entries = (*handle)->new_number;
-       repl->size = (*handle)->entries.size;
-       memcpy(repl->hook_entry, (*handle)->info.hook_entry,
-              sizeof(repl->hook_entry));
-       memcpy(repl->underflow, (*handle)->info.underflow,
-              sizeof(repl->underflow));
+       repl->num_entries = new_number;
+       repl->size = new_size;
+
        repl->num_counters = (*handle)->info.num_entries;
        repl->valid_hooks = (*handle)->info.valid_hooks;
-       memcpy(repl->entries, (*handle)->entries.entrytable,
-              (*handle)->entries.size);
+
+       DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
+               repl->num_entries, repl->size, repl->num_counters);
+
+       ret = iptcc_compile_table(*handle, repl);
+       if (ret < 0) {
+               errno = ret;
+               free(repl->counters);
+               free(repl);
+               return 0;
+       }
+
+
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_set_replace.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, repl, sizeof(*repl) + repl->size);
+                       close(fd);
+               }
+       }
+#endif
 
        if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
-                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+                      sizeof(*repl) + repl->size) < 0) {
                free(repl->counters);
                free(repl);
                free(newcounters);
@@ -1785,50 +2109,65 @@ TC_COMMIT(TC_HANDLE_T *handle)
 
        /* Put counters back. */
        strcpy(newcounters->name, (*handle)->info.name);
-       newcounters->num_counters = (*handle)->new_number;
-       for (i = 0; i < (*handle)->new_number; i++) {
-               unsigned int mappos = (*handle)->counter_map[i].mappos;
-               switch ((*handle)->counter_map[i].maptype) {
-               case COUNTER_MAP_NOMAP:
-                       newcounters->counters[i]
-                               = ((STRUCT_COUNTERS){ 0, 0 });
-                       break;
-
-               case COUNTER_MAP_NORMAL_MAP:
-                       /* Original read: X.
-                        * Atomic read on replacement: X + Y.
-                        * Currently in kernel: Z.
-                        * Want in kernel: X + Y + Z.
-                        * => Add in X + Y
-                        * => Add in replacement read.
-                        */
-                       newcounters->counters[i] = repl->counters[mappos];
-                       break;
-
-               case COUNTER_MAP_ZEROED:
-                       /* Original read: X.
-                        * Atomic read on replacement: X + Y.
-                        * Currently in kernel: Z.
-                        * Want in kernel: Y + Z.
-                        * => Add in Y.
-                        * => Add in (replacement read - original read).
-                        */
-                       subtract_counters(&newcounters->counters[i],
-                                         &repl->counters[mappos],
-                                         &index2entry(*handle, i)->counters);
-                       break;
-
-               case COUNTER_MAP_SET:
-                       /* Want to set counter (iptables-restore) */
-
-                       memcpy(&newcounters->counters[i],
-                              &index2entry(*handle, i)->counters,
-                              sizeof(STRUCT_COUNTERS));
+       newcounters->num_counters = new_number;
+
+       list_for_each_entry(c, &(*handle)->chains, list) {
+               struct rule_head *r;
+
+               /* Builtin chains have their own counters */
+               if (iptcc_is_builtin(c)) {
+                       DEBUGP("counter for chain-index %u: ", c->foot_index);
+                       switch(c->counter_map.maptype) {
+                       case COUNTER_MAP_NOMAP:
+                               counters_nomap(newcounters, c->foot_index);
+                               break;
+                       case COUNTER_MAP_NORMAL_MAP:
+                               counters_normal_map(newcounters, repl,
+                                                   c->foot_index, 
+                                                   c->counter_map.mappos);
+                               break;
+                       case COUNTER_MAP_ZEROED:
+                               counters_map_zeroed(newcounters, repl,
+                                                   c->foot_index, 
+                                                   c->counter_map.mappos,
+                                                   &c->counters);
+                               break;
+                       case COUNTER_MAP_SET:
+                               counters_map_set(newcounters, c->foot_index,
+                                                &c->counters);
+                               break;
+                       }
+               }
 
-                       break;
+               list_for_each_entry(r, &c->rules, list) {
+                       DEBUGP("counter for index %u: ", r->index);
+                       switch (r->counter_map.maptype) {
+                       case COUNTER_MAP_NOMAP:
+                               counters_nomap(newcounters, r->index);
+                               break;
+
+                       case COUNTER_MAP_NORMAL_MAP:
+                               counters_normal_map(newcounters, repl,
+                                                   r->index, 
+                                                   r->counter_map.mappos);
+                               break;
+
+                       case COUNTER_MAP_ZEROED:
+                               counters_map_zeroed(newcounters, repl,
+                                                   r->index,
+                                                   r->counter_map.mappos,
+                                                   &r->entry->counters);
+                               break;
+
+                       case COUNTER_MAP_SET:
+                               counters_map_set(newcounters, r->index,
+                                                &r->entry->counters);
+                               break;
+                       }
                }
        }
 
+
 #ifdef KERNEL_64_USERSPACE_32
        {
                /* Kernel will think that pointer should be 64-bits, and get
@@ -1844,6 +2183,17 @@ TC_COMMIT(TC_HANDLE_T *handle)
        }
 #endif /* KERNEL_64_USERSPACE_32 */
 
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_set_add_counters.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, newcounters, counterlen);
+                       close(fd);
+               }
+       }
+#endif
+
        if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
                       newcounters, counterlen) < 0) {
                free(repl->counters);
diff --git a/libiptc/linux_list.h b/libiptc/linux_list.h
new file mode 100644 (file)
index 0000000..abdcf88
--- /dev/null
@@ -0,0 +1,723 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({     type __dummy; \
+       typeof(x) __dummy2; \
+       (void)(&__dummy == &__dummy2); \
+       1; \
+})
+
+#define prefetch(x)            1
+
+/* empty define to make this work in userspace -HW */
+#define smp_wmb()
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+               struct list_head * prev, struct list_head * next)
+{
+       new->next = next;
+       new->prev = prev;
+       smp_wmb();
+       next->prev = new;
+       prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+       __list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+                                       struct list_head *head)
+{
+       __list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry.  Instead, either synchronize_kernel()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+       struct list_head *next = head->next;
+       return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+                                struct list_head *head)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+       struct list_head *at = head->next;
+
+       first->prev = head;
+       head->next = first;
+
+       last->next = at;
+       at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+               pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each     -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev  -       iterate over a list backwards
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+               pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe  -       iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
+                    prefetch(pos->member.prev);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member),  \
+                    prefetch(pos->member.prev))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ *                     list_for_each_entry_continue
+ * @pos:       the type * to use as a start point
+ * @head:      the head of the list
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+       ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue -      iterate over list of given type
+ *                     continuing after existing point
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member)                \
+       for (pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop counter.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_rcu   -       iterate over an rcu-protected list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+               pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __list_for_each_rcu(pos, head) \
+       for (pos = (head)->next; pos != (head); \
+               pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * list_for_each_safe_rcu      -       iterate over an rcu-protected list safe
+ *                                     against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * list_for_each_entry_rcu     -       iterate over rcu list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member)                     \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    ({ smp_read_barrier_depends(); 0;}),               \
+                    prefetch(pos->member.next))
+
+
+/**
+ * list_for_each_continue_rcu  -       iterate over an rcu-protected list
+ *                     continuing after existing point.
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+       for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+               (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+       struct hlist_node *first;
+};
+
+struct hlist_node {
+       struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+       return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+       return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+       struct hlist_node *next = n->next;
+       struct hlist_node **pprev = n->pprev;
+       *pprev = next;
+       if (next)
+               next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+       __hlist_del(n);
+       n->next = LIST_POISON1;
+       n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+       __hlist_del(n);
+       n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+       if (n->pprev)  {
+               __hlist_del(n);
+               INIT_HLIST_NODE(n);
+       }
+}
+
+#define hlist_del_rcu_init hlist_del_init
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+       n->next = first;
+       if (first)
+               first->pprev = &n->next;
+       h->first = n;
+       n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry(), but only if smp_read_barrier_depends()
+ * is used to prevent memory-consistency problems on Alpha CPUs.
+ * Regardless of the type of CPU, the list-traversal primitive
+ * must be guarded by rcu_read_lock().
+ *
+ * OK, so why don't we have an hlist_for_each_entry_rcu()???
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+                                       struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+       n->next = first;
+       n->pprev = &h->first;
+       smp_wmb();
+       if (first)
+               first->pprev = &n->next;
+       h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       n->pprev = next->pprev;
+       n->next = next;
+       next->pprev = &n->next;
+       *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       next->next = n->next;
+       n->next = next;
+       next->pprev = &n->next;
+
+       if(next->next)
+               next->next->pprev  = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+       for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+            pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+       for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+            pos = n)
+
+/**
+ * hlist_for_each_entry        - iterate over list of given type
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)                   \
+       for (pos = (head)->first;                                        \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)                \
+       for (pos = (pos)->next;                                          \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)                    \
+       for (; pos && ({ prefetch(pos->next); 1;}) &&                    \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @n:         another &struct hlist_node to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member)           \
+       for (pos = (head)->first;                                        \
+            pos && ({ n = pos->next; 1; }) &&                           \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member)               \
+       for (pos = (head)->first;                                        \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
+
+#endif
diff --git a/libiptc/linux_stddef.h b/libiptc/linux_stddef.h
new file mode 100644 (file)
index 0000000..56416f1
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _LINUX_STDDEF_H
+#define _LINUX_STDDEF_H
+
+#undef NULL
+#if defined(__cplusplus)
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({     type __dummy; \
+       typeof(x) __dummy2; \
+       (void)(&__dummy == &__dummy2); \
+       1; \
+})
+
+
+#endif