This commit was manufactured by cvs2svn to create branch planetlab-3_2-branch
authorPlanet-Lab Support <support@planet-lab.org>
Mon, 10 Jan 2005 20:29:30 +0000 (20:29 +0000)
committerPlanet-Lab Support <support@planet-lab.org>
Mon, 10 Jan 2005 20:29:30 +0000 (20:29 +0000)
'planetlab-3_2-branch'.

26 files changed:
.cvsignore [new file with mode: 0644]
extensions/.addrtype-test [new file with mode: 0755]
extensions/.cvsignore [new file with mode: 0644]
extensions/.pool-test [new file with mode: 0755]
extensions/libipt_POOL.c [new file with mode: 0644]
extensions/libipt_pool.c [new file with mode: 0644]
extensions/libipt_pool.c.orig [new file with mode: 0644]
include/libippool/ip_pool_support.h [new file with mode: 0644]
ip6tables.8 [new file with mode: 0644]
ippool/Makefile [new file with mode: 0644]
ippool/ippool.c [new file with mode: 0644]
ippool/libippool.c [new file with mode: 0644]
iptables-config [new file with mode: 0644]
iptables.8 [new file with mode: 0644]
iptables.init [new file with mode: 0755]
iptables.spec [new file with mode: 0644]
libiptc/.cvsignore [new file with mode: 0644]
libiptc/libip6tc.c.orig [new file with mode: 0644]
libiptc2/foo.diff [new file with mode: 0644]
libiptc2/libip4tc.c [new file with mode: 0644]
libiptc2/libip6tc.c [new file with mode: 0644]
libiptc2/libiptc.c [new file with mode: 0644]
libiptc2/libiptc.cvs.c [new file with mode: 0644]
libiptc2/libiptc2.c [new file with mode: 0644]
libiptc2/linux_list.h [new file with mode: 0644]
libiptc2/linux_listhelp.h [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..7bdbcc3
--- /dev/null
@@ -0,0 +1,8 @@
+.makefirst
+ip6tables
+ip6tables-restore
+ip6tables-save
+iptables
+iptables-restore
+iptables-save
+*.d
diff --git a/extensions/.addrtype-test b/extensions/.addrtype-test
new file mode 100755 (executable)
index 0000000..cda582b
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+if test -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_addrtype.h; then
+       echo "addrtype"
+fi
diff --git a/extensions/.cvsignore b/extensions/.cvsignore
new file mode 100644 (file)
index 0000000..a438335
--- /dev/null
@@ -0,0 +1 @@
+*.d
diff --git a/extensions/.pool-test b/extensions/.pool-test
new file mode 100755 (executable)
index 0000000..54d04bf
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ip_pool.h ] && echo pool POOL
diff --git a/extensions/libipt_POOL.c b/extensions/libipt_POOL.c
new file mode 100644 (file)
index 0000000..931dcfd
--- /dev/null
@@ -0,0 +1,155 @@
+/* Shared library add-on to iptables to add IP pool 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_pool.h>
+#include <linux/netfilter_ipv4/ipt_pool.h>
+
+#include <libippool/ip_pool_support.h>
+
+/* FIXME --RR */
+#define ip_pool_init           ip_POOL_init
+#define ip_pool_get_index      ip_POOL_get_index
+#define ip_pool_get_name       ip_POOL_get_name
+#include "../ippool/libippool.c"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"POOL v%s options:\n"
+" --add-srcip <pool>\n"
+" --del-srcip <pool>\n"
+" --add-dstip <pool>\n"
+" --del-dstip <pool>\n"
+"                              add/del src/dst IP from pool.\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "add-srcip", 1, 0, '1' },
+       { "del-srcip", 1, 0, '2' },
+       { "add-dstip", 1, 0, '3' },
+       { "del-dstip", 1, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *target, unsigned int *nfcache)
+{
+       struct ipt_pool_info *ipi = (struct ipt_pool_info *) target->data;
+
+       ipi->src = ipi->dst = IP_POOL_NONE;
+       ipi->flags = 0;
+
+       /* Can't cache this */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* 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_pool_info *ipi = (struct ipt_pool_info *) (*target)->data;
+       switch (c) {
+       case '1':       /* --add-srcip <pool> */
+               ipi->src = ip_pool_get_index(optarg);
+               ipi->flags &= ~IPT_POOL_DEL_SRC;
+               break;
+       case '2':       /* --del-srcip <pool> */
+               ipi->src = ip_pool_get_index(optarg);
+               ipi->flags |= IPT_POOL_DEL_SRC;
+               break;
+       case '3':       /* --add-dstip <pool> */
+               ipi->dst = ip_pool_get_index(optarg);
+               ipi->flags &= ~IPT_POOL_DEL_DST;
+               break;
+       case '4':       /* --del-dstip <pool> */
+               ipi->dst = ip_pool_get_index(optarg);
+               ipi->flags |= IPT_POOL_DEL_DST;
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       char buf[256];
+       struct ipt_pool_info *ipi = (struct ipt_pool_info *) target->data;
+
+       printf("POOL");
+       if (ipi->src != IP_POOL_NONE) {
+               printf(" --%s-srcip %s",
+                       (ipi->flags & IPT_POOL_DEL_SRC) ? "del" : "add",
+                       ip_pool_get_name(buf, sizeof(buf), ipi->src, numeric));
+       }
+       if (ipi->dst != IP_POOL_NONE) {
+               printf(" --%s-dstip %s",
+                       (ipi->flags & IPT_POOL_DEL_DST) ? "del" : "add",
+                       ip_pool_get_name(buf, sizeof(buf), ipi->dst, numeric));
+       }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       char buf[256];
+       struct ipt_pool_info *ipi = (struct ipt_pool_info *) target->data;
+
+       printf("-j POOL");
+       if (ipi->src != IP_POOL_NONE) {
+               printf(" --%s-srcip %s",
+                       (ipi->flags & IPT_POOL_DEL_SRC) ? "del" : "add",
+                       ip_pool_get_name(buf, sizeof(buf), ipi->src, 0));
+       }
+       if (ipi->dst != IP_POOL_NONE) {
+               printf(" --%s-dstip %s",
+                       (ipi->flags & IPT_POOL_DEL_DST) ? "del" : "add",
+                       ip_pool_get_name(buf, sizeof(buf), ipi->dst, 0));
+       }
+}
+
+static
+struct iptables_target ipt_pool_target
+= { NULL,
+    "POOL",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_pool_info)),
+    IPT_ALIGN(sizeof(struct ipt_pool_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&ipt_pool_target);
+}
diff --git a/extensions/libipt_pool.c b/extensions/libipt_pool.c
new file mode 100644 (file)
index 0000000..082d76c
--- /dev/null
@@ -0,0 +1,144 @@
+/* Shared library add-on to iptables to add IP address pool matching. */
+#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_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_pool.h>
+
+#include <libippool/ip_pool_support.h>
+
+/* FIXME --RR */
+#include "../ippool/libippool.c"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"pool v%s options:\n"
+" [!] --srcpool NAME|INDEX\n"
+" [!] --dstpool NAME|INDEX\n"
+"                      Pool index (or name from %s) to match\n"
+"\n", IPTABLES_VERSION, IPPOOL_CONF);
+}
+
+static struct option opts[] = {
+       { "srcpool", 1, 0, '1' },
+       { "dstpool", 1, 0, '2' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       info->src = IP_POOL_NONE;
+       info->dst = IP_POOL_NONE;
+       info->flags = 0;
+       /* Can't cache this - XXX */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* 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_pool_info *info =
+               (struct ipt_pool_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               info->src = ip_pool_get_index(argv[optind-1]);
+               if (invert) info->flags |= IPT_POOL_INV_SRC;
+               *flags = 1;
+               break;
+       case '2':
+               check_inverse(optarg, &invert, &optind, 0);
+               info->dst = ip_pool_get_index(argv[optind-1]);
+               if (invert) info->flags |= IPT_POOL_INV_DST;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified --srcpool or --dstpool. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify either `--srcpool or --dstpool'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       char buf[256];
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       if (info->src != IP_POOL_NONE)
+               printf("%ssrcpool %s ",
+                       (info->flags & IPT_POOL_INV_SRC) ? "!" : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+       if (info->dst != IP_POOL_NONE)
+               printf("%sdstpool %s ",
+                       (info->flags & IPT_POOL_INV_DST) ? "!" : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       char buf[256];
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       if (info->src != IP_POOL_NONE)
+               printf("%s--srcpool %s ",
+                       (info->flags & IPT_POOL_INV_SRC) ? "! " : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+       if (info->dst != IP_POOL_NONE)
+               printf("%s--dstpool %s ",
+                       (info->flags & IPT_POOL_INV_DST) ? "! " : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+static
+struct iptables_match pool
+= { NULL,
+    "pool",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_pool_info)),
+    IPT_ALIGN(sizeof(struct ipt_pool_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&pool);
+}
diff --git a/extensions/libipt_pool.c.orig b/extensions/libipt_pool.c.orig
new file mode 100644 (file)
index 0000000..666599d
--- /dev/null
@@ -0,0 +1,144 @@
+/* Shared library add-on to iptables to add IP address pool matching. */
+#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_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_pool.h>
+
+#include <libippool/ip_pool_support.h>
+
+/* FIXME --RR */
+#include "../ippool/libippool.c"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"pool v%s options:\n"
+" [!] --srcpool NAME|INDEX\n"
+" [!] --dstpool NAME|INDEX\n"
+"                      Pool index (or name from %s) to match\n"
+"\n", IPTABLES_VERSION, IPPOOL_CONF);
+}
+
+static struct option opts[] = {
+       { "srcpool", 1, 0, '1' },
+       { "dstpool", 1, 0, '2' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       info->src = IP_POOL_NONE;
+       info->dst = IP_POOL_NONE;
+       info->flags = 0;
+       /* Can't cache this - XXX */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* 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_pool_info *info =
+               (struct ipt_pool_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               info->src = ip_pool_get_index(argv[optind-1]);
+               if (invert) info->flags |= IPT_POOL_INV_SRC;
+               *flags = 1;
+               break;
+       case '2':
+               check_inverse(optarg, &invert, &optind, 0);
+               info->dst = ip_pool_get_index(argv[optind-1]);
+               if (invert) info->flags |= IPT_POOL_INV_DST;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified --srcpool or --dstpool. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify either `--srcpool or --dstpool'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       char buf[256];
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       if (info->src != IP_POOL_NONE)
+               printf("%ssrcpool %s ",
+                       (info->flags & IPT_POOL_INV_SRC) ? "!" : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+       if (info->dst != IP_POOL_NONE)
+               printf("%sdstpool %s ",
+                       (info->flags & IPT_POOL_INV_DST) ? "!" : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       char buf[256];
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       if (info->src != IP_POOL_NONE)
+               printf("%s--srcpool %s",
+                       (info->flags & IPT_POOL_INV_SRC) ? "! " : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+       if (info->dst != IP_POOL_NONE)
+               printf("%s--dstpool %s",
+                       (info->flags & IPT_POOL_INV_DST) ? "! " : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+static
+struct iptables_match pool
+= { NULL,
+    "pool",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_pool_info)),
+    IPT_ALIGN(sizeof(struct ipt_pool_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&pool);
+}
diff --git a/include/libippool/ip_pool_support.h b/include/libippool/ip_pool_support.h
new file mode 100644 (file)
index 0000000..4a87f02
--- /dev/null
@@ -0,0 +1,26 @@
+/* support function prototypes for IP pool management (config file, mostly) */
+#ifndef _IP_POOL_SUPPORT_H
+#define _IP_POOL_SUPPORT_H
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_pool.h>
+
+#ifndef IPPOOL_CONF
+#define IPPOOL_CONF "/etc/ippool.conf"
+#endif
+
+/* called just to draw in this support .o */
+void ip_pool_init(void);
+
+/* given a pool name (or number), return pool index, possibly reading .conf */
+ip_pool_t ip_pool_get_index(char *name);
+
+/* given a pool index, and a buffer to store a name, search for the index
+ * in the .conf file, and give the textual name, if present; if not, the
+ * numeric index is returned. If numeric_flag == 1, the numeric index is
+ * always returned
+ */
+char *ip_pool_get_name(char *buf, int size, ip_pool_t index, int numeric_flag);
+
+#endif /*_IP_POOL_SUPPORT_H*/
diff --git a/ip6tables.8 b/ip6tables.8
new file mode 100644 (file)
index 0000000..53a310c
--- /dev/null
@@ -0,0 +1,821 @@
+.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.
+.SS tcp
+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.
+.SS udp
+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.
+.SS ipv6-icmp
+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
+.SS mac
+.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.
+.SS limit
+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.
+.SS multiport
+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.
+.SS mark
+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).
+.SS owner
+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.
+.\" .SS state
+.\" 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 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.
+.\" .SS unclean
+.\" This module takes no options, but attempts to match packets which seem
+.\" malformed or unusual.  This is regarded as experimental.
+.\" .SS tos
+.\" 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.
+.SH TARGET EXTENSIONS
+ip6tables can use extended target modules: the following are included
+in the standard distribution.
+.SS LOG
+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.
+.SS MARK
+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"
+.SS REJECT
+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).
+.\" .SS TOS
+.\" 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
+.\" .br
+.\" iptables -j TOS -h
+.\" .br
+.\" to see the list of valid TOS names.
+.\" .SS MIRROR
+.\" 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.
+.\" .SS SNAT
+.\" 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 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.
+.\" .SS DNAT
+.\" 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
+.\" 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.
+.\" .SS MASQUERADE
+.\" 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" .
+.\" .SS REDIRECT
+.\" 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 alters the destination IP address to send the packet to
+.\" the machine itself (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" .
+.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 ..
diff --git a/ippool/Makefile b/ippool/Makefile
new file mode 100644 (file)
index 0000000..100c2e4
--- /dev/null
@@ -0,0 +1,18 @@
+EXTRAS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_pool.h ] && echo ippool/libippool.a ippool/ippool)
+
+ifndef TOPLEVEL_INCLUDED
+local:
+       cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS)
+
+else
+EXTRA_DEPENDS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_pool.h ] && echo ippool/libippool.d)
+
+ippool/libippool.a: ippool/libippool.a(ippool/libippool.o)
+
+ippool/libippool.d: %.d: %.c
+       @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.a($*.o):@' > $@
+
+ippool/ippool.o: ippool/ippool.c
+ippool/ippool: ippool/ippool.o ippool/libippool.a
+       $(CC) $(CFLAGS) -o $@ $^
+endif
diff --git a/ippool/ippool.c b/ippool/ippool.c
new file mode 100644 (file)
index 0000000..3a680d3
--- /dev/null
@@ -0,0 +1,589 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <linux/netfilter_ipv4/ip_pool.h>
+#include <libippool/ip_pool_support.h>
+
+/* translate errnos, as returned by our *sockopt() functions */
+static char *errno2msg(int op, int err)
+{
+       switch(err) {
+       case ERANGE:
+               return "Address out of pool range";
+       }
+       return strerror(err);
+}
+
+static void usage(char *msg)
+{
+       if (msg) fprintf(stderr, "ERROR: %s\n", msg);
+       fprintf(stderr,
+"Usage: ippool [-LADCNF] [POOL] [args]\n"
+"The POOL argument is either a number, or a name from %s\n"
+, IPPOOL_CONF);
+       fprintf(stderr,
+"ippool [-n] [-l|-L POOL] [-u] [-v]\n"
+       "\tLists all (-l), or a single (-L) pool.\n"
+       "\tWith -u, summarized usage counts are listed.\n"
+       "\tWith -v, each pool membership is shown, one per line.\n"
+"ippool [-n] [-f|-F [POOL]]\n"
+       "\tflushes POOL (or all pools.)\n"
+"ippool [-n] [-x|-X [POOL]]\n"
+       "\tremoves POOL (or all pools.)\n"
+"ippool [-n] -B\n"
+       "\tcreates all fully specified pools found in the config file.\n"
+"ippool [-n] -N POOL [-t type] [FIRST LAST]\n"
+       "\tcreates POOL of IP addresses FIRST to LAST (inclusive).  If a\n"
+       "\tpool name from the config file %s is used, type and\n"
+       "\taddress information can be defined there.  The -t argument\n"
+       "\tgives the type (default bitmap).\n"
+"ippool [-n] -A POOL ADDR\n"
+       "\tadds ADDR to POOL\n"
+"ippool [-n] -D POOL ADDR\n"
+       "\tremoves ADDR from POOL\n"
+"ippool [-n] -C POOL ADDR\n"
+       "\ttests ADDR against membership in POOL\n"
+, IPPOOL_CONF);
+       exit(1);
+}
+
+/* config file parsing */
+
+#define IP_POOL_T_NONE         0
+#define IP_POOL_T_BITMAP       1
+
+static int conf_type = IP_POOL_T_NONE;
+static unsigned long conf_addr = 0;
+static unsigned long conf_addr2 = 0;
+
+#define SCAN_EOF (IP_POOL_NONE-1)
+
+static ip_pool_t get_index_line(
+       FILE *fp,
+       char **namep,
+       char **typep,
+       char **argp
+) {
+       char *p;
+       ip_pool_t index;
+       static char buf[256];
+
+       if (namep) *namep = 0;
+       if (typep) *typep = 0;
+       if (argp) *argp = 0;
+
+       if (!fgets(buf, sizeof(buf), fp)) return SCAN_EOF;
+
+       p = strtok(buf, " \t\n");
+       if (!p || *p == '#') return IP_POOL_NONE;
+       index = atoi(p);
+
+       p = strtok(0, " \t\n");
+       if (!p || *p == '#') return index;
+       if (namep) *namep = p;
+
+       p = strtok(0, " \t\n");
+       if (!p || *p == '#') return index;
+       if (typep) *typep = p;
+
+       p = strtok(0, "#\n");
+       if (argp) *argp = p;
+
+       return index;
+}
+
+static ip_pool_t get_index(char *name)
+{
+       FILE *fp;
+       char *poolname, *type, *arg, *p;
+       ip_pool_t res;
+
+       if (isdigit(*name))
+               return atoi(name);
+       fp = fopen(IPPOOL_CONF, "r");
+       if (!fp) {
+               fprintf(stderr, "cannot open %s - no pool names", IPPOOL_CONF);
+               exit(1);
+       }
+       while (SCAN_EOF != (res=get_index_line(fp, &poolname, &type, &arg))) {
+               if (poolname && 0 == strcmp(poolname, name)) {
+                       if (!type || (0 == strcmp(type, "bitmap"))) {
+                               conf_type = IP_POOL_T_BITMAP;
+                               p = strtok(arg, " \t");
+                               if (p) {
+                                       conf_addr = inet_addr(p);
+                                       p = strtok(0, " \t");
+                                       if (p)
+                                               conf_addr2 = inet_addr(p);
+                                       else
+                                               conf_addr = 0;
+                               }
+                       }
+                       break;
+               }
+       }
+       fclose(fp);
+       if (res == SCAN_EOF) {
+               fprintf(stderr, "pool '%s' not found in %s\n",
+                       name, IPPOOL_CONF);
+               exit(1);
+       }
+       return res;
+}
+
+static char *get_name(ip_pool_t index)
+{
+       FILE *fp;
+       static char sbuf[256];
+       int ok = 0;
+
+       fp = fopen(IPPOOL_CONF, "r");
+       if (fp) {
+               while (fgets(sbuf, sizeof(sbuf), fp)) {
+                       char *p = strtok(sbuf, " \t\n");
+
+                       if (!p || *p == '#') continue;
+                       if (index != atoi(p)) continue;
+                       p = strtok(0, " \t\n");
+                       if (!p || *p == '#') continue;
+                       memmove(sbuf, p, strlen(p)+1);
+                       ok = 1;
+                       break;
+               }
+               fclose(fp);
+       }
+       if (!ok) sprintf(sbuf, "%d", index);
+       return sbuf;
+}
+
+/* user/kernel interaction */
+
+static int fd = -1;
+static int flag_n = 0;         /* do not do anything; just brag about it */
+static int flag_v = 0;         /* be verbose (list members) */
+static int flag_q = 0;         /* be quiet */
+static int flag_u = 0;         /* show usage counts in listings */
+static char *flag_t = "bitmap";        /* pool type */
+
+static ip_pool_t high_nr(void)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_HIGH_NR;
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               fprintf(stderr,
+                       "IP_POOL_HIGH_NR failed: %s\n",
+                       errno2msg(IP_POOL_HIGH_NR, errno));
+               exit(1);
+       }
+       return req.index;
+}
+
+static void do_list(ip_pool_t pool)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+       u_int32_t first_ip;
+       u_int32_t last_ip;
+
+       req.op = IP_POOL_LOOKUP;
+       req.index = pool;
+       req.addr = req.addr2 = 0;
+       reqlen = sizeof(req);
+       if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               struct in_addr ina;
+               ina.s_addr = req.addr;
+               printf("%s %s", get_name(req.index), inet_ntoa(ina));
+               ina.s_addr = req.addr2;
+               printf(" %s", inet_ntoa(ina));
+               first_ip = ntohl(req.addr);
+               last_ip = ntohl(req.addr2);
+               if (flag_u) {
+                       req.op = IP_POOL_USAGE;
+                       req.index = pool;
+                       reqlen = sizeof(req);
+                       if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL,
+                                               &req, &reqlen)) {
+                               printf(" %d %d", req.addr, req.addr2);
+                       } else {
+                               printf(" - -");
+                       }
+               }
+               printf("\n");
+               if (flag_v) {
+                       while (first_ip <= last_ip) {
+                               req.op = IP_POOL_TEST_ADDR;
+                               req.index = pool;
+                               ina.s_addr = req.addr = htonl(first_ip);
+                               reqlen = sizeof(req);
+                               if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL,
+                                                       &req, &reqlen)) {
+                                       if (req.addr) printf("\t%s\n",
+                                                       inet_ntoa(ina));
+                               }
+                               first_ip++;
+                       }
+               }
+       }
+}
+
+static void do_list_all(void)
+{
+       ip_pool_t i, highest = high_nr();
+
+       for (i=0; i<=highest; i++)
+               do_list(i);
+}
+
+static void do_flush(ip_pool_t pool)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_FLUSH;
+       req.index = pool;
+       if (flag_n) {
+               printf("ippool -F %d\n", req.index);
+               return;
+       }
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               int err = errno;
+               fprintf(stderr,
+                       "IP_POOL_FLUSH %s failed: %s\n",
+                       get_name(pool), errno2msg(IP_POOL_FLUSH, err));
+               exit(1);
+       }
+}
+
+static void do_flush_all(void)
+{
+       ip_pool_t i, highest = high_nr();
+
+       for (i=0; i<=highest; i++)
+               do_flush(i);
+}
+
+static void do_destroy(ip_pool_t pool)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_DESTROY;
+       req.index = pool;
+       if (flag_n) {
+               printf("ippool -X %d\n", req.index);
+               return;
+       }
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               int err = errno;
+               fprintf(stderr,
+                       "IP_POOL_DESTROY %s failed: %s\n",
+                       get_name(pool), errno2msg(IP_POOL_DESTROY, err));
+               exit(1);
+       }
+}
+
+static void do_destroy_all(void)
+{
+       ip_pool_t i, highest = high_nr();
+
+       for (i=0; i<=highest; i++)
+               do_destroy(i);
+}
+
+static int do_adddel(ip_pool_t pool, char *straddr, int op)
+{
+       struct ip_pool_request req;
+       int res;
+       int reqlen = sizeof(req);
+
+       req.op = op;
+       req.index = pool;
+       req.addr = inet_addr(straddr);
+       req.addr2 = 0;
+       if (flag_n) {
+               printf("ippool -%c %s %s\n",
+                               (op == IP_POOL_ADD_ADDR) ? 'A' : 'D',
+                               get_name(req.index), straddr);
+               return 0;
+       }
+       res = getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen);
+       if (0 > res) {
+               int err = errno;
+               fprintf(stderr,
+                       "IP_POOL_ADD/DEL %s in %s failed: %s\n",
+                       straddr, get_name(pool), errno2msg(op, err));
+               exit(1);
+       }
+       if (!flag_q)
+               printf("%s %s %d %d\n", get_name(pool), straddr, req.addr,
+                       op == IP_POOL_ADD_ADDR ? 1 : 0);
+       return req.addr;
+}
+
+static int do_check(ip_pool_t pool, char *straddr)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_TEST_ADDR;
+       req.index = pool;
+       req.addr = inet_addr(straddr);
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               int err = errno;
+               fprintf(stderr,
+                       "IP_POOL_TEST_ADDR %s in %s failed: %s\n",
+                       straddr, get_name(pool),
+                       errno2msg(IP_POOL_TEST_ADDR, err));
+               exit(1);
+       }
+       if (!flag_q)
+               printf("%s %s %d\n", get_name(req.index), straddr, req.addr);
+       return !req.addr;
+}
+
+static void do_new(ip_pool_t pool, int argc, char **argv)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_INIT;
+       req.index = pool;
+       if (argc >= 2) {
+               conf_type = IP_POOL_T_BITMAP;
+               conf_addr = inet_addr(argv[0]);
+               conf_addr2 = inet_addr(argv[1]);
+       }
+       if (conf_type != IP_POOL_T_BITMAP || conf_addr == 0 || conf_addr2 == 0)
+               usage("bad pool specification");
+       req.addr = conf_addr;
+       req.addr2 = conf_addr2;
+       if (flag_n) {
+               printf("ippool -N %s [-T %s] ...\n", get_name(pool),
+                                       conf_type == IP_POOL_T_BITMAP
+                                               ? "bitmap"
+                                               : "???");
+               return;
+       }
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               struct in_addr ina;
+               int err = errno;
+               ina.s_addr = conf_addr;
+               fprintf(stderr,
+                       "IP_POOL_INIT %s [%s-",
+                       get_name(pool), inet_ntoa(ina));
+               ina.s_addr = conf_addr2;
+               fprintf(stderr,
+                       "%s] failed: %s\n",
+                       inet_ntoa(ina), errno2msg(IP_POOL_INIT, err));
+               exit(1);
+       }
+}
+
+static void do_new_all(void)
+{
+       FILE *fp;
+       char *poolname, *type, *arg, *p;
+       int res;
+
+       fp = fopen(IPPOOL_CONF, "r");
+       if (!fp) {
+               fprintf(stderr, "cannot open %s - no pool names", IPPOOL_CONF);
+               exit(1);
+       }
+       while (SCAN_EOF != (res=get_index_line(fp, &poolname, &type, &arg))) {
+               if (poolname && type && (0 == strcmp(type, "bitmap"))) {
+                       conf_type = IP_POOL_T_BITMAP;
+                       p = strtok(arg, " \t");
+                       if (p) {
+                               conf_addr = inet_addr(p);
+                               p = strtok(0, " \t");
+                               if (p)
+                                       conf_addr2 = inet_addr(p);
+                               else
+                                       conf_addr = 0;
+                       }
+                       if (conf_addr != 0) {
+                               if (flag_v)
+                                       printf("ippool -N %s (%s) [%s]\n",
+                                               poolname, type, arg);
+                               do_new(res, 0, 0);
+                       }
+               }
+       }
+       fclose(fp);
+}
+
+int main(int argc, char **argv)
+{
+       int opt;
+       int op;
+#define OP_NONE                0
+#define OP_LIST                1
+#define OP_LIST_ALL    2
+#define OP_FLUSH       3
+#define OP_FLUSH_ALL   4
+#define OP_DESTROY     5
+#define OP_DESTROY_ALL 6
+#define OP_ADD         7
+#define OP_DEL         8
+#define OP_CHECK       9
+#define OP_NEW         10
+#define OP_NEW_ALL     11
+#define OP_HIGHEST     12
+       char *op_pool;
+
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               fprintf(stderr, "cannot get DGRAM socket: %s\n",
+                       strerror(errno));
+               exit(1);
+       }
+       op_pool = 0;
+       op = OP_NONE;
+       /* GRRR. I thought getopt() would allow an "L*" specifier, for an -L
+        * taking an optional argument. Does not work. Bad.
+        * Adding -l for -L without argument, also -f/-F and -x/-X.
+        */
+       while (EOF != (opt=getopt( argc, argv, "HhnvuqA:D:C:N:t:L:F:X:lfxB")))
+       switch(opt) {
+               case 'l':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_LIST_ALL;
+                       break;
+               case 'L':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_LIST;
+                       op_pool = optarg;
+                       break;
+               case 'f':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_FLUSH_ALL;
+                       break;
+               case 'F':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_FLUSH;
+                       op_pool = optarg;
+                       break;
+               case 'x':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_DESTROY_ALL;
+                       break;
+               case 'X':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_DESTROY;
+                       op_pool = optarg;
+                       break;
+               case 'A':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_ADD;
+                       op_pool = optarg;
+                       break;
+               case 'D':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_DEL;
+                       op_pool = optarg;
+                       break;
+               case 'C':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_CHECK;
+                       op_pool = optarg;
+                       break;
+               case 'B':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_NEW_ALL;
+                       break;
+               case 'N':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_NEW;
+                       op_pool = optarg;
+                       break;
+               case 'H':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_HIGHEST;
+                       break;
+               case 't':
+                       flag_t = optarg;
+                       break;
+               case 'n':
+                       flag_n = 1;
+                       break;
+               case 'v':
+                       flag_v = 1;
+                       break;
+               case 'u':
+                       flag_u = 1;
+                       break;
+               case 'q':
+                       flag_q = 1;
+                       break;
+               case 'h':
+                       usage(0);
+               default:
+                       usage("bad option");
+       }
+       if (op == OP_NONE)
+               usage("no operation specified");
+       if (op == OP_LIST_ALL) {
+               do_list_all();
+               return 0;
+       }
+       if (op == OP_LIST) {
+               do_list(get_index(op_pool));
+               return 0;
+       }
+       if (op == OP_FLUSH_ALL) {
+               do_flush_all();
+               return 0;
+       }
+       if (op == OP_FLUSH) {
+               do_flush(get_index(op_pool));
+               return 0;
+       }
+       if (op == OP_DESTROY_ALL) {
+               do_destroy_all();
+               return 0;
+       }
+       if (op == OP_DESTROY) {
+               do_destroy(get_index(op_pool));
+               return 0;
+       }
+       if (op == OP_CHECK) {
+               if (optind >= argc)
+                       usage("missing address to check");
+               return do_check(get_index(op_pool), argv[optind]);
+       }
+       if (op == OP_NEW_ALL) {
+               do_new_all();
+               return 0;
+       }
+       if (op == OP_NEW) {
+               do_new(get_index(op_pool), argc-optind, argv+optind);
+               return 0;
+       }
+       if (op == OP_ADD) {
+               if (optind >= argc)
+                       usage("missing address to add");
+               return do_adddel(get_index(op_pool),
+                               argv[optind], IP_POOL_ADD_ADDR);
+       }
+       if (op == OP_DEL) {
+               if (optind >= argc)
+                       usage("missing address to delete");
+               return do_adddel(get_index(op_pool),
+                               argv[optind], IP_POOL_DEL_ADDR);
+       }
+       if (op == OP_HIGHEST) {
+               printf("%d\n", high_nr());
+               return 0;
+       }
+       usage("no operation specified");
+       return 0;
+}
diff --git a/ippool/libippool.c b/ippool/libippool.c
new file mode 100644 (file)
index 0000000..7f3ac76
--- /dev/null
@@ -0,0 +1,72 @@
+/* support functions for ip_pool modules */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <libippool/ip_pool_support.h>
+
+void ip_pool_init(void)
+{
+}
+
+ip_pool_t ip_pool_get_index(char *name)
+{
+       FILE *fp;
+       char buf[256];
+
+       if (isdigit(*name))
+               return atoi(name);
+       fp = fopen(IPPOOL_CONF, "r");
+       if (!fp) exit_error(PARAMETER_PROBLEM,
+                       "cannot open %s - no pool names", IPPOOL_CONF);
+       while (fgets(buf, sizeof(buf), fp)) {
+               char *p = strtok(buf, " \t\n");
+               ip_pool_t index;
+
+               if (!p || *p == '#') continue;
+               index = atoi(p);
+               p = strtok(0, " \t\n");
+               if (p && 0 == strcmp(p, name)) {
+                       fclose(fp);
+                       return index;
+               }
+       }
+       exit_error(PARAMETER_PROBLEM,
+               "pool '%s' not found in %s\n", name, IPPOOL_CONF);
+}
+
+char *ip_pool_get_name(char *buf, int size, ip_pool_t index, int numeric)
+{
+       FILE *fp;
+       int ok = 0;
+
+       /* make sure we have enough room, at least for a %d */
+       if (size < 16)
+               exit_error(PARAMETER_PROBLEM,
+                       "ip_pool_support:get_name buf too small (%d vs. 16)\n",
+                       size);
+       if (numeric)
+               goto do_numeric;
+       fp = fopen(IPPOOL_CONF, "r");
+       if (fp) {
+               while (fgets(buf, size, fp)) {
+                       char *p = strtok(buf, " \t\n");
+
+                       if (!p || *p == '#') continue;
+                       if (index != atoi(p)) continue;
+                       p = strtok(0, " \t\n");
+                       if (!p || *p == '#') continue;
+                       memmove(buf, p, strlen(p)+1);
+                       ok = 1;
+                       break;
+               }
+               fclose(fp);
+       }
+       if (!ok) {
+do_numeric:
+               sprintf(buf, "%d", index);
+       }
+       return buf;
+}
diff --git a/iptables-config b/iptables-config
new file mode 100644 (file)
index 0000000..80e37fb
--- /dev/null
@@ -0,0 +1,37 @@
+# Load additional iptables modules (nat helpers)
+#   Default: -none-
+# Space separated list of nat helpers (e.g. 'ip_nat_ftp ip_nat_irc'), which
+# are loaded after the firewall rules are applied. Options for the helpers are
+# stored in /etc/modules.conf.
+#IPTABLES_MODULES=""
+
+# Unload modules on restart and stop
+#   Value: yes|no,  default: yes
+# This option has to be 'yes' to get to a sane state for a firewall
+# restart or stop. Only set to 'no' if there are problems unloading netfilter
+# modules.
+#IPTABLES_MODULES_UNLOAD="yes"
+
+# Save current firewall rules on stop.
+#   Value: yes|no,  default: no
+# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets stopped
+# (e.g. on system shutdown).
+#IPTABLES_SAVE_ON_STOP="no"
+
+# Save current firewall rules on restart.
+#   Value: yes|no,  default: no
+# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets
+# restarted.
+#IPTABLES_SAVE_ON_RESTART="no"
+
+# Save (and restore) rule and chain counter.
+#   Value: yes|no,  default: no
+# Save counters for rules and chains to /etc/sysconfig/iptables if
+# 'service iptables save' is called or on stop or restart if SAVE_ON_STOP or
+# SAVE_ON_RESTART is enabled.
+#IPTABLES_SAVE_COUNTER="no"
+
+# Numeric status output
+#   Value: yes|no,  default: no
+# Print IP addresses and port numbers in numeric format in the status output.
+#IPTABLES_STATUS_NUMERIC="no"
diff --git a/iptables.8 b/iptables.8
new file mode 100644 (file)
index 0000000..b79f1ec
--- /dev/null
@@ -0,0 +1,1072 @@
+.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 coming into the box itself),
+.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).
+.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 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 ,
+.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.
+.SS ah
+This module matches the SPIs in AH header of IPSec packets.
+.TP
+.BR "--ahspi " "[!] \fIspi\fP[:\fIspi\fP]"
+.SS conntrack
+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)
+.SS dscp
+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.
+.SS esp
+This module matches the SPIs in ESP header of IPSec packets.
+.TP
+.BR "--espspi " "[!] \fIspi\fP[:\fIspi\fP]"
+.SS helper
+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
+.SS icmp
+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
+.SS length
+This module matches the length of a packet against a specific value
+or range of values.
+.TP
+.BR "--length " "\fIlength\fP[:\fIlength\fP]"
+.SS limit
+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.
+.SS mac
+.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.
+.SS mark
+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).
+.SS multiport
+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.
+.SS owner
+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)
+.SS physdev
+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.
+.SS pkttype
+This module matches the link-layer packet type.
+.TP
+.BI "--pkt-type " "[\fIunicast\fP|\fIbroadcast\fP|\fImulticast\fP]"
+.SS state
+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.
+.SS tcp
+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 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.
+.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.
+.SS tos
+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.
+.SS ttl
+This module matches the time to live field in the IP header.
+.TP
+.BI "--ttl " "ttl"
+Matches the given TTL value.
+.SS udp
+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.
+.SS unclean
+This module takes no options, but attempts to match packets which seem
+malformed or unusual.  This is regarded as experimental.
+.SH TARGET EXTENSIONS
+iptables can use extended target modules: the following are included
+in the standard distribution.
+.SS DNAT
+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.
+.SS DSCP
+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.
+.SS ECN
+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" .
+.SS LOG
+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.
+.SS MARK
+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"
+.SS MASQUERADE
+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" .
+.SS MIRROR
+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.
+.SS REDIRECT
+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 alters the destination IP address to send the packet to
+the machine itself (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" .
+.SS REJECT
+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
+.SS SNAT
+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.
+.SS TCPMSS
+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.
+.SS TOS
+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.
+.SS ULOG
+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
+.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 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, 
+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 ..
diff --git a/iptables.init b/iptables.init
new file mode 100755 (executable)
index 0000000..5c99246
--- /dev/null
@@ -0,0 +1,320 @@
+#!/bin/sh
+#
+# iptables     Start iptables firewall
+#
+# chkconfig: 2345 08 92
+# description: Starts, stops and saves iptables firewall
+#
+# config: /etc/sysconfig/iptables
+# config: /etc/sysconfig/iptables-config
+
+# Source function library.
+. /etc/init.d/functions
+
+IPTABLES=iptables
+IPTABLES_DATA=/etc/sysconfig/$IPTABLES
+IPTABLES_CONFIG=/etc/sysconfig/${IPTABLES}-config
+IPV=${IPTABLES%tables} # ip for ipv4 | ip6 for ipv6
+PROC_IPTABLES_NAMES=/proc/net/${IPV}_tables_names
+VAR_SUBSYS_IPTABLES=/var/lock/subsys/$IPTABLES
+
+if [ ! -x /sbin/$IPTABLES ]; then
+    echo -n $"/sbin/$IPTABLES does not exist."; warning; echo
+    exit 0
+fi
+
+if lsmod 2>/dev/null | grep -q ipchains ; then
+    echo -n $"ipchains and $IPTABLES can not be used together."; warning; echo
+    exit 0
+fi
+
+# Old or new modutils
+/sbin/modprobe --version 2>&1 | grep -q module-init-tools \
+    && NEW_MODUTILS=1 \
+    || NEW_MODUTILS=0
+
+# Default firewall configuration:
+IPTABLES_MODULES=""
+IPTABLES_MODULES_UNLOAD="yes"
+IPTABLES_SAVE_ON_STOP="no"
+IPTABLES_SAVE_ON_RESTART="no"
+IPTABLES_SAVE_COUNTER="no"
+IPTABLES_STATUS_NUMERIC="no"
+
+# Load firewall configuration.
+[ -f "$IPTABLES_CONFIG" ] && . "$IPTABLES_CONFIG"
+
+rmmod_r() {
+    # Unload module with all referring modules.
+    # At first all referring modules will be unloaded, then the module itself.
+    local mod=$1
+    local ret=0
+    local ref=
+
+    # Get referring modules.
+    # New modutils have another output format.
+    [ $NEW_MODUTILS = 1 ] \
+       && ref=`lsmod | awk "/^${mod}/ { print \\\$4; }" | tr ',' ' '` \
+       || ref=`lsmod | grep ^${mod} | cut -d "[" -s -f 2 | cut -d "]" -s -f 1`
+
+    # recursive call for all referring modules
+    for i in $ref; do
+       rmmod_r $i
+       let ret+=$?;
+    done
+
+    # Unload module.
+    # The extra test is for 2.6: The module might have autocleaned,
+    # after all referring modules are unloaded.
+    if grep -q "^${mod}" /proc/modules ; then
+       modprobe -r $mod > /dev/null 2>&1
+       let ret+=$?;
+    fi
+
+    return $ret
+}
+
+flush_n_delete() {
+    # Flush firewall rules and delete chains.
+    [ -e "$PROC_IPTABLES_NAMES" ] || return 1
+
+    # Check if firewall is configured (has tables)
+    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
+    [ -z "$tables" ] && return 1
+
+    echo -n $"Flushing firewall rules: "
+    ret=0
+    # For all tables
+    for i in $tables; do
+        # Flush firewall rules.
+       $IPTABLES -t $i -F;
+       let ret+=$?;
+
+        # Delete firewall chains.
+       $IPTABLES -t $i -X;
+       let ret+=$?;
+
+       # Set counter to zero.
+       $IPTABLES -t $i -Z;
+       let ret+=$?;
+    done
+
+    [ $ret -eq 0 ] && success || failure
+    echo
+    return $ret
+}
+
+set_policy() {
+    # Set policy for configured tables.
+    policy=$1
+
+    # Check if iptable module is loaded
+    [ ! -e "$PROC_IPTABLES_NAMES" ] && return 1
+
+    # Check if firewall is configured (has tables)
+    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
+    [ -z "$tables" ] && return 1
+
+    echo -n $"Setting chains to policy $policy: "
+    ret=0
+    for i in $tables; do
+       echo -n "$i "
+       case "$i" in
+           filter)
+                $IPTABLES -t filter -P INPUT $policy \
+                   && $IPTABLES -t filter -P OUTPUT $policy \
+                   && $IPTABLES -t filter -P FORWARD $policy \
+                   || let ret+=1
+               ;;
+           nat)
+               $IPTABLES -t nat -P PREROUTING $policy \
+                   && $IPTABLES -t nat -P POSTROUTING $policy \
+                   && $IPTABLES -t nat -P OUTPUT $policy \
+                   || let ret+=1
+               ;;
+           mangle)
+               $IPTABLES -t mangle -P PREROUTING $policy \
+                   && $IPTABLES -t mangle -P POSTROUTING $policy \
+                   && $IPTABLES -t mangle -P INPUT $policy \
+                   && $IPTABLES -t mangle -P OUTPUT $policy \
+                   && $IPTABLES -t mangle -P FORWARD $policy \
+                   || let ret+=1
+               ;;
+           *)
+               let ret+=1
+               ;;
+        esac
+    done
+
+    [ $ret -eq 0 ] && success || failure
+    echo
+    return $ret
+}
+
+start() {
+    # Do not start if there is no config file.
+    [ -f "$IPTABLES_DATA" ] || return 1
+
+    echo -n $"Applying $IPTABLES firewall rules: "
+
+    OPT=
+    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
+
+    $IPTABLES-restore $OPT $IPTABLES_DATA
+    if [ $? -eq 0 ]; then
+       success; echo
+    else
+       failure; echo; return 1
+    fi
+    
+    # Load additional modules (helpers)
+    if [ -n "$IPTABLES_MODULES" ]; then
+       echo -n $"Loading additional $IPTABLES modules: "
+       ret=0
+       for mod in $IPTABLES_MODULES; do
+           echo -n "$mod "
+           modprobe $mod > /dev/null 2>&1
+           let ret+=$?;
+       done
+       [ $ret -eq 0 ] && success || failure
+       echo
+    fi
+    
+    touch $VAR_SUBSYS_IPTABLES
+    return $ret
+}
+
+stop() {
+    # Do not stop if iptables module is not loaded.
+    [ -e "$PROC_IPTABLES_NAMES" ] || return 1
+
+    flush_n_delete
+    set_policy ACCEPT
+    
+    if [ "x$IPTABLES_MODULES_UNLOAD" = "xyes" ]; then
+       echo -n $"Unloading $IPTABLES modules: "
+       ret=0
+       rmmod_r ${IPV}_tables
+       let ret+=$?;
+       rmmod_r ${IPV}_conntrack
+       let ret+=$?;
+       [ $ret -eq 0 ] && success || failure
+       echo
+    fi
+    
+    rm -f $VAR_SUBSYS_IPTABLES
+    return $ret
+}
+
+save() {
+    # Check if iptable module is loaded
+    [ ! -e "$PROC_IPTABLES_NAMES" ] && return 1
+
+    # Check if firewall is configured (has tables)
+    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
+    [ -z "$tables" ] && return 1
+
+    echo -n $"Saving firewall rules to $IPTABLES_DATA: "
+
+    OPT=
+    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
+
+    ret=0
+    TMP_FILE=`/bin/mktemp -q /tmp/$IPTABLES.XXXXXX` \
+       && chmod 600 "$TMP_FILE" \
+       && $IPTABLES-save $OPT > $TMP_FILE 2>/dev/null \
+       && size=`stat -c '%s' $TMP_FILE` && [ $size -gt 0 ] \
+       || ret=1
+    if [ $ret -eq 0 ]; then
+       if [ -e $IPTABLES_DATA ]; then
+           cp -f $IPTABLES_DATA $IPTABLES_DATA.save \
+               && chmod 600 $IPTABLES_DATA.save \
+               || ret=1
+       fi
+       if [ $ret -eq 0 ]; then
+           cp -f $TMP_FILE $IPTABLES_DATA \
+               && chmod 600 $IPTABLES_DATA \
+               || ret=1
+       fi
+    fi
+    [ $ret -eq 0 ] && success || failure
+    echo
+    rm -f $TMP_FILE
+    return $ret
+}
+
+status() {
+    # Do not print status if lockfile is missing and iptables modules are not 
+    # loaded.
+    # Check if iptable module is loaded
+    if [ ! -f "$VAR_SUBSYS_IPTABLES" ]; then
+       echo $"Firewall is stopped."
+       return 1
+    fi
+
+    # Check if firewall is configured (has tables)
+    if [ ! -e "$PROC_IPTABLES_NAMES" ]; then
+       echo $"Firewall is not configured. "
+       return 1
+    fi
+    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
+    if [ -z "$tables" ]; then
+       echo $"Firewall is not configured. "
+       return 1
+    fi
+
+    NUM=
+    [ "x$IPTABLES_STATUS_NUMERIC" = "xyes" ] && NUM="-n"
+
+    for table in $tables; do
+       echo $"Table: $table"
+       $IPTABLES -t $table --list $NUM && echo
+    done
+
+    return 0
+}
+
+restart() {
+    [ "x$IPTABLES_SAVE_ON_RESTART" = "xyes" ] && save
+    stop
+    start
+}
+
+case "$1" in
+    start)
+       stop
+       start
+       RETVAL=$?
+       ;;
+    stop)
+       [ "x$IPTABLES_SAVE_ON_STOP" = "xyes" ] && save
+       stop
+       RETVAL=$?
+       ;;
+    restart)
+       restart
+       RETVAL=$?
+       ;;
+    condrestart)
+       [ -e "$VAR_SUBSYS_IPTABLES" ] && restart
+       ;;
+    status)
+       status
+       RETVAL=$?
+       ;;
+    panic)
+       flush_n_delete
+       set_policy DROP
+       RETVAL=$?
+        ;;
+    save)
+       save
+       RETVAL=$?
+       ;;
+    *)
+       echo $"Usage: $0 {start|stop|restart|condrestart|status|panic|save}"
+       exit 1
+       ;;
+esac
+
+exit $RETVAL
diff --git a/iptables.spec b/iptables.spec
new file mode 100644 (file)
index 0000000..a6ddcef
--- /dev/null
@@ -0,0 +1,371 @@
+%define name iptables
+%define version 1.2.9
+%define release 2.3.1.1.planetlab%{?date:.%{date}}
+
+Vendor: PlanetLab
+Packager: PlanetLab Central <support@planet-lab.org>
+Distribution: PlanetLab 3.0
+URL: http://cvs.planet-lab.org/cvs/iptables
+
+%define build_devel 1
+%define linux_header 0
+
+Name: %{name}
+Summary: Tools for managing Linux kernel packet filtering capabilities.
+Version: %{version}
+Release: %{release}
+Source: http://www.netfilter.org/%{name}-%{version}.tar.bz2
+%define SOURCE1 iptables.init
+%define SOURCE2 iptables-config
+%if %{linux_header}
+Source3: netfilter-2.4.20.tar.gz
+%endif
+Patch2: iptables-1.2.8-nolibnsl.patch
+Patch3: iptables-1.2.8-print_type.patch
+Patch4: iptables-1.2.9-netlink.patch
+Patch5: iptables-1.2.9-selinux.patch
+Patch6: iptables-1.2.9-counters.patch
+Group: System Environment/Base
+#URL: http://www.netfilter.org/
+BuildRoot: %{_tmppath}/%{name}-buildroot
+License: GPL
+BuildPrereq: /usr/bin/perl
+Requires: kernel >= 2.4.20
+Requires(post,postun): chkconfig
+Prefix: %{_prefix}
+
+%package ipv6
+Summary: IPv6 support for iptables.
+Group: System Environment/Base
+Requires: %{name} = %{version}
+
+%if %{build_devel}
+%package devel
+Summary: Development package for iptables.
+Group: System Environment/Base
+Requires: %{name} = %{version}
+%endif
+
+%description
+The iptables utility controls the network packet filtering code in the
+Linux kernel. If you need to set up firewalls and/or IP masquerading,
+you should install this package.
+
+%description ipv6
+The iptables package contains IPv6 (the next version of the IP
+protocol) support for iptables. Iptables controls the Linux kernel
+network packet filtering code, allowing you to set up firewalls and IP
+masquerading. 
+
+Install iptables-ipv6 if you need to set up firewalling for your
+network and you are using ipv6.
+
+%if %{build_devel}
+%description devel
+The iptables utility controls the network packet filtering code in the
+Linux kernel. If you need to set up firewalls and/or IP masquerading,
+you should install this package.
+%endif
+
+%prep
+rm -rf %{buildroot}
+
+%setup -q
+
+# Put it to a reasonable place
+find . -type f -exec perl -pi -e "s,/usr,%{prefix},g" {} \;
+
+%build
+TOPDIR=`pwd`
+OPT="$RPM_OPT_FLAGS -I$TOPDIR/include"
+# bootstrap to avoid BuildRequires of kernel-source
+for KERNEL_DIR in $RPM_BUILD_DIR/linux-* /lib/modules/`uname -r`/build /usr ; do
+    if [ -f $KERNEL_DIR/include/linux/version.h ] ; then
+       break
+    fi
+done
+make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib}
+make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib} iptables-save iptables-restore
+make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib} ip6tables-save ip6tables-restore
+
+%install
+# bootstrap to avoid BuildRequires of kernel-source
+for KERNEL_DIR in $RPM_BUILD_DIR/linux-* /lib/modules/`uname -r`/build /usr ; do
+    if [ -f $KERNEL_DIR/include/linux/version.h ] ; then
+       break
+    fi
+done
+make install DESTDIR=%{buildroot} KERNEL_DIR=/usr BINDIR=/sbin LIBDIR=/%{_lib} MANDIR=%{_mandir}
+%if %{build_devel}
+make install-devel DESTDIR=%{buildroot} KERNEL_DIR=/usr BINDIR=/sbin LIBDIR=%{_libdir} MANDIR=%{_mandir}
+%endif
+cp ip{6,}tables-{save,restore} $RPM_BUILD_ROOT/sbin
+cp iptables-*.8 $RPM_BUILD_ROOT%{_mandir}/man8
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+install -c -m755 %{SOURCE1} $RPM_BUILD_ROOT/etc/rc.d/init.d/iptables
+sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE1} > ip6tables.init
+install -c -m755 ip6tables.init $RPM_BUILD_ROOT/etc/rc.d/init.d/ip6tables
+mkdir -p $RPM_BUILD_ROOT/etc/sysconfig
+install -c -m755 %{SOURCE2} $RPM_BUILD_ROOT/etc/sysconfig/iptables-config
+sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE2} > ip6tables-config
+install -c -m755 ip6tables-config $RPM_BUILD_ROOT/etc/sysconfig/ip6tables-config
+
+%clean
+rm -rf $RPM_BUILD_ROOT 
+
+%post
+/sbin/chkconfig --add iptables
+
+%preun
+if [ "$1" = 0 ]; then
+       /sbin/chkconfig --del iptables
+fi
+
+%post ipv6
+/sbin/chkconfig --add ip6tables
+
+%preun ipv6
+if [ "$1" = 0 ]; then
+       /sbin/chkconfig --del ip6tables
+fi
+
+%files
+%defattr(-,root,root,0755)
+%doc COPYING INSTALL INCOMPATIBILITIES
+%config %attr(0755,root,root) /etc/rc.d/init.d/iptables
+%config(noreplace) %attr(0600,root,root) /etc/sysconfig/iptables-config
+/sbin/iptables*
+%{_mandir}/man8/iptables*
+%dir /%{_lib}/iptables
+/%{_lib}/iptables/libipt*
+
+%files ipv6
+%defattr(-,root,root,0755)
+%config %attr(0755,root,root) /etc/rc.d/init.d/ip6tables
+%config(noreplace) %attr(0600,root,root) /etc/sysconfig/ip6tables-config
+/sbin/ip6tables*
+%{_mandir}/man8/ip6tables*
+/%{_lib}/iptables/libip6t*
+
+%if %{build_devel}
+%files devel
+%defattr(-,root,root,0755)
+%{_includedir}/libipq.h
+%{_libdir}/libipq.a
+%{_libdir}/libiptc.a
+%{_mandir}/man3/*
+%endif
+
+%changelog
+* Tue Mar 02 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Thu Feb 26 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-2.3
+- fixed iptables-restore -c fault if there are no counters (#116421)
+
+* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Sun Jan  25 2004 Dan Walsh <dwalsh@redhat.com> 1.2.9-1.2
+- Close File descriptors to prevent SELinux error message
+
+* Wed Jan  7 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-1.1
+- rebuild
+
+* Wed Dec 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.9-1
+- vew version 1.2.9
+- new config options in ipXtables-config:
+  IPTABLES_MODULES_UNLOAD
+- more documentation in ipXtables-config
+- fix for netlink security issue in libipq (devel package)
+- print fix for libipt_icmp (#109546)
+
+* Thu Oct 23 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-13
+- marked all messages in iptables init script for translation (#107462)
+- enabled devel package (#105884, #106101)
+- bumped build for fedora for libipt_recent.so (#106002)
+
+* Tue Sep 23 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-12.1
+- fixed lost udp port range in ip6tables-save (#104484)
+- fixed non numeric multiport port output in ipXtables-savs
+
+* Mon Sep 22 2003 Florian La Roche <Florian.LaRoche@redhat.de> 1.2.8-11
+- do not link against -lnsl
+
+* Wed Sep 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-10
+- made variables in rmmod_r local
+
+* Tue Jul 22 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-9
+- fixed permission for init script
+
+* Sat Jul 19 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-8
+- fixed save when iptables file is missing and iptables-config permissions
+
+* Tue Jul  8 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-7
+- fixes for ip6tables: module unloading, setting policy only for existing 
+  tables
+
+* Thu Jul  3 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-6
+- IPTABLES_SAVE_COUNTER defaults to no, now
+- install config file in /etc/sysconfig
+- exchange unload of ip_tables and ip_conntrack
+- fixed start function
+
+* Wed Jul  2 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-5
+- new config option IPTABLES_SAVE_ON_RESTART
+- init script: new status, save and restart
+- fixes #44905, #65389, #80785, #82860, #91040, #91560 and #91374
+
+* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-4
+- new config option IPTABLES_STATUS_NUMERIC
+- cleared IPTABLES_MODULES in iptables-config
+
+* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-3
+- new init scripts
+
+* Sat Jun 28 2003 Florian La Roche <Florian.LaRoche@redhat.de>
+- remove check for very old kernel versions in init scripts
+- sync up both init scripts and remove some further ugly things
+- add some docu into rpm
+
+* Thu Jun 26  2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-2
+- rebuild
+
+* Mon Jun 16 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-1
+- update to 1.2.8
+
+* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
+- rebuilt
+
+* Mon Jan 13 2003 Bill Nottingham <notting@redhat.com> 1.2.7a-1
+- update to 1.2.7a
+- add a plethora of bugfixes courtesy Michael Schwendt <mschewndt@yahoo.com>
+
+* Fri Dec 13 2002 Elliot Lee <sopwith@redhat.com> 1.2.6a-3
+- Fix multilib
+
+* Wed Aug 07 2002 Karsten Hopp <karsten@redhat.de>
+- fixed iptables and ip6tables initscript output, based on #70511
+- check return status of all iptables calls, not just the last one
+  in a 'for' loop.
+
+* Mon Jul 29 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.6a-1
+- 1.2.6a (bugfix release, #69747)
+
+* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Thu May 23 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Mar  4 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-3
+- Add some fixes from CVS, fixing bug #60465
+
+* Tue Feb 12 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-2
+- Merge ip6tables improvements from Ian Prowell <iprowell@prowell.org>
+  #59402
+- Update URL (#59354)
+- Use /sbin/chkconfig rather than chkconfig in %postun script
+
+* Fri Jan 11 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-1
+- 1.2.5
+
+* Wed Jan 09 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Nov  5 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-2
+- Fix %preun script
+
+* Tue Oct 30 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-1
+- Update to 1.2.4 (various fixes, including security fixes; among others:
+  #42990, #50500, #53325, #54280)
+- Fix init script (#31133)
+
+* Mon Sep  3 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.3-1
+- 1.2.3 (5 security fixes, some other fixes)
+- Fix updating (#53032)
+
+* Mon Aug 27 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-4
+- Fix #50990
+- Add some fixes from current CVS; should fix #52620
+
+* Mon Jul 16 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-3
+- Add some fixes from the current CVS tree; fixes #49154 and some IPv6
+  issues
+
+* Tue Jun 26 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-2
+- Fix iptables-save reject-with (#45632), Patch from Michael Schwendt
+  <mschwendt@yahoo.com>
+
+* Tue May  8 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-1
+- 1.2.2
+
+* Wed Mar 21 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.2.1a, fixes #28412, #31136, #31460, #31133
+
+* Thu Mar  1 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Yet another initscript fix (#30173)
+- Fix the fixes; they fixed some issues but broke more important
+  stuff :/ (#30176)
+
+* Tue Feb 27 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Fix up initscript (#27962)
+- Add fixes from CVS to iptables-{restore,save}, fixing #28412
+
+* Fri Feb 09 2001 Karsten Hopp <karsten@redhat.de>
+- create /etc/sysconfig/iptables mode 600 (same problem as #24245)
+
+* Mon Feb 05 2001 Karsten Hopp <karsten@redhat.de>
+- fix bugzilla #25986 (initscript not marked as config file)
+- fix bugzilla #25962 (iptables-restore)
+- mv chkconfig --del from postun to preun
+
+* Thu Feb  1 2001 Trond Eivind Glomsrød <teg@redhat.com>
+- Fix check for ipchains
+
+* Mon Jan 29 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Some fixes to init scripts
+
+* Wed Jan 24 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Add some fixes from CVS, fixes among other things Bug #24732
+
+* Wed Jan 17 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Add missing man pages, fix up init script (Bug #17676)
+
+* Mon Jan 15 2001 Bill Nottingham <notting@redhat.com>
+- add init script
+
+* Mon Jan 15 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.2
+- fix up ipv6 split
+- add init script
+- Move the plugins from /usr/lib/iptables to /lib/iptables.
+  This needs to work before /usr is mounted...
+- Use -O1 on alpha (compiler bug)
+
+* Sat Jan  6 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.1.2
+- Add IPv6 support (in separate package)
+
+* Thu Aug 17 2000 Bill Nottingham <notting@redhat.com>
+- build everywhere
+
+* Tue Jul 25 2000 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.1.1
+
+* Thu Jul 13 2000 Prospector <bugzilla@redhat.com>
+- automatic rebuild
+
+* Tue Jun 27 2000 Preston Brown <pbrown@redhat.com>
+- move iptables to /sbin.
+- excludearch alpha for now, not building there because of compiler bug(?)
+
+* Fri Jun  9 2000 Bill Nottingham <notting@redhat.com>
+- don't obsolete ipchains either
+- update to 1.1.0
+
+* Mon Jun  4 2000 Bill Nottingham <notting@redhat.com>
+- remove explicit kernel requirement
+
+* Tue May  2 2000 Bernhard Rosenkränzer <bero@redhat.com>
+- initial package
diff --git a/libiptc/.cvsignore b/libiptc/.cvsignore
new file mode 100644 (file)
index 0000000..a438335
--- /dev/null
@@ -0,0 +1 @@
+*.d
diff --git a/libiptc/libip6tc.c.orig b/libiptc/libip6tc.c.orig
new file mode 100644 (file)
index 0000000..9a78a5a
--- /dev/null
@@ -0,0 +1,449 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING       NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP6_LOCAL_IN
+#define HOOK_FORWARD           NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET    struct ip6t_entry_target
+#define STRUCT_ENTRY           struct ip6t_entry
+#define STRUCT_ENTRY_MATCH     struct ip6t_entry_match
+#define STRUCT_GETINFO         struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES     struct ip6t_get_entries
+#define STRUCT_COUNTERS                struct ip6t_counters
+#define STRUCT_COUNTERS_INFO   struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE         struct ip6t_replace
+
+#define STRUCT_TC_HANDLE       struct ip6tc_handle
+#define TC_HANDLE_T            ip6tc_handle_t
+
+#define ENTRY_ITERATE          IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ip6t_get_target
+
+#define ERROR_TARGET           IP6T_ERROR_TARGET
+#define NUMHOOKS               NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL         ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries6
+#define TC_IS_CHAIN            ip6tc_is_chain
+#define TC_FIRST_CHAIN         ip6tc_first_chain
+#define TC_NEXT_CHAIN          ip6tc_next_chain
+#define TC_FIRST_RULE          ip6tc_first_rule
+#define TC_NEXT_RULE           ip6tc_next_rule
+#define TC_GET_TARGET          ip6tc_get_target
+#define TC_BUILTIN             ip6tc_builtin
+#define TC_GET_POLICY          ip6tc_get_policy
+#define TC_INSERT_ENTRY                ip6tc_insert_entry
+#define TC_REPLACE_ENTRY       ip6tc_replace_entry
+#define TC_APPEND_ENTRY                ip6tc_append_entry
+#define TC_DELETE_ENTRY                ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY    ip6tc_delete_num_entry
+#define TC_CHECK_PACKET                ip6tc_check_packet
+#define TC_FLUSH_ENTRIES       ip6tc_flush_entries
+#define TC_ZERO_ENTRIES                ip6tc_zero_entries
+#define TC_ZERO_COUNTER                ip6tc_zero_counter
+#define TC_READ_COUNTER                ip6tc_read_counter
+#define TC_SET_COUNTER         ip6tc_set_counter
+#define TC_CREATE_CHAIN                ip6tc_create_chain
+#define TC_GET_REFERENCES      ip6tc_get_references
+#define TC_DELETE_CHAIN                ip6tc_delete_chain
+#define TC_RENAME_CHAIN                ip6tc_rename_chain
+#define TC_SET_POLICY          ip6tc_set_policy
+#define TC_GET_RAW_SOCKET      ip6tc_get_raw_socket
+#define TC_INIT                        ip6tc_init
+#define TC_FREE                        ip6tc_free
+#define TC_COMMIT              ip6tc_commit
+#define TC_STRERROR            ip6tc_strerror
+
+#define TC_AF                  AF_INET6
+#define TC_IPPROTO             IPPROTO_IPV6
+
+#define SO_SET_REPLACE         IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES         IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION         IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET                IP6T_STANDARD_TARGET
+#define LABEL_RETURN           IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT           IP6TC_LABEL_ACCEPT
+#define LABEL_DROP             IP6TC_LABEL_DROP
+#define LABEL_QUEUE            IP6TC_LABEL_QUEUE
+
+#define ALIGN                  IP6T_ALIGN
+#define RETURN                 IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+       int l, i;
+       for (l = 0; l < 128; l++) {
+               if (BIT6(a, l) == 0)
+                       break;
+       }
+       for (i = l + 1; i < 128; i++) {
+               if (BIT6(a, i) == 1)
+                       return -1;
+       }
+       return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+       size_t i;
+       char buf[40];
+       int len;
+       struct ip6t_entry_target *t;
+       
+       printf("Entry %u (%lu):\n", entry2index(handle, e),
+              entry2offset(handle, e));
+       puts("SRC IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.smsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       puts("DST IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.dmsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       printf("Interface: `%s'/", e->ipv6.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ipv6.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ipv6.proto);
+       if (e->ipv6.flags & IP6T_F_TOS)
+               printf("TOS: %u\n", e->ipv6.tos);
+       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);
+       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);
+
+       t = ip6t_get_target(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == IP6T_RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static int
+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. */
+       if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+           || a->ipv6.proto != b->ipv6.proto
+           || a->ipv6.tos != b->ipv6.tos
+           || a->ipv6.flags != b->ipv6.flags
+           || a->ipv6.invflags != b->ipv6.invflags)
+               return 0;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+                       return 0;
+               if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+                   != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+                       return 0;
+               if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+                       return 0;
+               if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+                   != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+                       return 0;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return 0;
+
+       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 1;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ipv6); i++)
+               if (((char *)ipv6)[i])
+                       break;
+
+       return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP6_LOCAL_IN
+                          | 1 << NF_IP6_FORWARD
+                          | 1 << NF_IP6_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_FORWARD
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+               }
+       } else {
+                fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ipv6));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               printf("target_size=%u, align=%u\n",
+                       t->target.u.target_size, ALIGN(sizeof(*t)));
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               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]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+
+#if 0
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc2/foo.diff b/libiptc2/foo.diff
new file mode 100644 (file)
index 0000000..af69eb5
--- /dev/null
@@ -0,0 +1,391 @@
+--- libiptc.c  2003-06-30 18:26:59.000000000 +0200
++++ libiptc.c  2003-06-30 18:27:24.000000000 +0200
+@@ -64,19 +61,35 @@
+       char error[TABLE_MAXNAMELEN];
+ };
+-struct chain_cache
++struct rule_head
+ {
++      struct list_head list;
++      
++      struct chain_head *chain;
++
++      unsigned int size;
++      STRUCT_ENTRY entry[0];
++}
++
++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;
++      struct list_head rules;
+ };
+ STRUCT_TC_HANDLE
+ {
+       /* Have changes been made? */
+       int changed;
++
++      struct list_head chains;
++      
++      struct chain_head *chain_iterator_cur;
++
++#if 0
+       /* Size in here reflects original state. */
+       STRUCT_GETINFO info;
+@@ -98,6 +111,7 @@
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+       STRUCT_GET_ENTRIES entries;
++#endif
+ };
+ static void
+@@ -375,173 +389,25 @@
+       }
+       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--;
+-                              if (i+1 >= 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 *
++/* Returns chain head if found, otherwise NULL. */
++static struct chain_head *
+ find_label(const char *name, TC_HANDLE_T handle)
+ {
+-      unsigned int i;
++      struct list_head *pos;
+-      if (handle->cache_chain_heads == NULL
+-          && !populate_cache(handle))
++      if (!handle->chains)
+               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];
++      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;
+@@ -594,34 +460,30 @@
+ const char *
+ TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+ {
+-      if ((*handle)->cache_chain_heads == NULL
+-          && !populate_cache(*handle))
+-              return NULL;
++      (*handle)->chain_iterator_cur = (*handle)->chains;
+-      (*handle)->cache_chain_iteration
+-              = &(*handle)->cache_chain_heads[0];
+-
+-      return (*handle)->cache_chain_iteration->name;
++      return (*handle)->chains.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 *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
++      (*handle)->chain_iterator_cur = next;
+-      if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
+-          == (*handle)->cache_num_chains)
++      if (next == (*handle)->chains)
+               return NULL;
+-      return (*handle)->cache_chain_iteration->name;
++      return next->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;
+       c = find_label(chain, *handle);
+       if (!c) {
+@@ -630,22 +492,26 @@
+       }
+       /* Empty chain: single return/policy rule */
+-      if (c->start_off == c->end_off)
++      if (list_empty(c->rules))
+               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;
++
++      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 = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
++
++      if (r == r->chain)
+               return NULL;
+-      return (void *)prev + prev->next_offset;
++      /* NOTE: prev is without any influence ! */
++      return r->entry;
+ }
+ #if 0
+@@ -773,7 +639,7 @@
+       return target_name(*handle, e);
+ }
+-static inline int
++static int
+ correct_verdict(STRUCT_ENTRY *e,
+               char *base,
+               unsigned int offset, int delta_offset)
+@@ -874,16 +740,8 @@
+       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);
+-              return 0;
+-      }
+-
++      if ((*handle)->cache_chain_heads)
++              free((*handle)->cache_chain_heads);
+       free(*handle);
+       *handle = newh;
+@@ -942,10 +800,6 @@
+       (*handle)->new_number -= num_rules;
+       (*handle)->entries.size -= rules_size;
+-      /* Fix the chain cache */
+-      if (!correct_cache(*handle, offset, -(int)rules_size))
+-              return 0;
+-
+       return set_verdict(offset, -(int)rules_size, handle);
+ }
+@@ -1449,7 +1303,6 @@
+               STRUCT_ENTRY ret;
+               STRUCT_STANDARD_TARGET target;
+       } newc;
+-      unsigned int destination;
+       iptc_fn = TC_CREATE_CHAIN;
+@@ -1487,21 +1340,11 @@
+               = 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,
++                         index2offset(*handle, (*handle)->new_number - 1),
+                          (*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;
+ }
+@@ -1629,11 +1472,6 @@
+       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);
+-
+       set_changed(*handle);
+       return 1;
diff --git a/libiptc2/libip4tc.c b/libiptc2/libip4tc.c
new file mode 100644 (file)
index 0000000..e012c08
--- /dev/null
@@ -0,0 +1,507 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libiptc.h"
+
+#define IP_VERSION     4
+#define IP_OFFSET      0x1FFF
+
+#define HOOK_PRE_ROUTING       NF_IP_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP_LOCAL_IN
+#define HOOK_FORWARD           NF_IP_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP_POST_ROUTING
+#ifdef NF_IP_DROPPING
+#define HOOK_DROPPING          NF_IP_DROPPING
+#endif
+
+#define STRUCT_ENTRY_TARGET    struct ipt_entry_target
+#define STRUCT_ENTRY           struct ipt_entry
+#define STRUCT_ENTRY_MATCH     struct ipt_entry_match
+#define STRUCT_GETINFO         struct ipt_getinfo
+#define STRUCT_GET_ENTRIES     struct ipt_get_entries
+#define STRUCT_COUNTERS                struct ipt_counters
+#define STRUCT_COUNTERS_INFO   struct ipt_counters_info
+#define STRUCT_STANDARD_TARGET struct ipt_standard_target
+#define STRUCT_REPLACE         struct ipt_replace
+
+#define STRUCT_TC_HANDLE       struct iptc_handle
+#define TC_HANDLE_T            iptc_handle_t
+
+#define ENTRY_ITERATE          IPT_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IPT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IPT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ipt_get_target
+
+#define ERROR_TARGET           IPT_ERROR_TARGET
+#define NUMHOOKS               NF_IP_NUMHOOKS
+
+#define IPT_CHAINLABEL         ipt_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries
+#define TC_IS_CHAIN            iptc_is_chain
+#define TC_FIRST_CHAIN         iptc_first_chain
+#define TC_NEXT_CHAIN          iptc_next_chain
+#define TC_FIRST_RULE          iptc_first_rule
+#define TC_NEXT_RULE           iptc_next_rule
+#define TC_GET_TARGET          iptc_get_target
+#define TC_BUILTIN             iptc_builtin
+#define TC_GET_POLICY          iptc_get_policy
+#define TC_INSERT_ENTRY                iptc_insert_entry
+#define TC_REPLACE_ENTRY       iptc_replace_entry
+#define TC_APPEND_ENTRY                iptc_append_entry
+#define TC_DELETE_ENTRY                iptc_delete_entry
+#define TC_DELETE_NUM_ENTRY    iptc_delete_num_entry
+#define TC_CHECK_PACKET                iptc_check_packet
+#define TC_FLUSH_ENTRIES       iptc_flush_entries
+#define TC_ZERO_ENTRIES                iptc_zero_entries
+#define TC_READ_COUNTER                iptc_read_counter
+#define TC_ZERO_COUNTER                iptc_zero_counter
+#define TC_SET_COUNTER         iptc_set_counter
+#define TC_CREATE_CHAIN                iptc_create_chain
+#define TC_GET_REFERENCES      iptc_get_references
+#define TC_DELETE_CHAIN                iptc_delete_chain
+#define TC_RENAME_CHAIN                iptc_rename_chain
+#define TC_SET_POLICY          iptc_set_policy
+#define TC_GET_RAW_SOCKET      iptc_get_raw_socket
+#define TC_INIT                        iptc_init
+#define TC_FREE                        iptc_free
+#define TC_COMMIT              iptc_commit
+#define TC_STRERROR            iptc_strerror
+
+#define TC_AF                  AF_INET
+#define TC_IPPROTO             IPPROTO_IP
+
+#define SO_SET_REPLACE         IPT_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IPT_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IPT_SO_GET_INFO
+#define SO_GET_ENTRIES         IPT_SO_GET_ENTRIES
+#define SO_GET_VERSION         IPT_SO_GET_VERSION
+
+#define STANDARD_TARGET                IPT_STANDARD_TARGET
+#define LABEL_RETURN           IPTC_LABEL_RETURN
+#define LABEL_ACCEPT           IPTC_LABEL_ACCEPT
+#define LABEL_DROP             IPTC_LABEL_DROP
+#define LABEL_QUEUE            IPTC_LABEL_QUEUE
+
+#define ALIGN                  IPT_ALIGN
+#define RETURN                 IPT_RETURN
+
+#include "libiptc.c"
+
+#define IP_PARTS_NATIVE(n)                     \
+(unsigned int)((n)>>24)&0xFF,                  \
+(unsigned int)((n)>>16)&0xFF,                  \
+(unsigned int)((n)>>8)&0xFF,                   \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+int
+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("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",
+              IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
+       printf("Interface: `%s'/", e->ip.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ip.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ip.proto);
+       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);
+       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);
+
+       t = GET_TARGET(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == -NF_QUEUE-1 ? "NF_QUEUE"
+                              : pos == RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static int
+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. */
+       if (a->ip.src.s_addr != b->ip.src.s_addr
+           || a->ip.dst.s_addr != b->ip.dst.s_addr
+           || a->ip.smsk.s_addr != b->ip.smsk.s_addr
+           || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr
+           || a->ip.proto != b->ip.proto
+           || a->ip.flags != b->ip.flags
+           || a->ip.invflags != b->ip.invflags)
+               return 0;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
+                       return 0;
+               if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
+                   != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
+                       return 0;
+               if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
+                       return 0;
+               if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
+                   != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
+                       return 0;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return 0;
+
+       mptr = matchmask + sizeof(STRUCT_ENTRY);
+       if (IPT_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 1;
+}
+
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
+               if (((u_int32_t *)ip)[i])
+                       return 0;
+
+       return 1;
+}
+
+static inline int
+check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
+{
+       assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
+       assert(ALIGN(m->u.match_size) == m->u.match_size);
+
+       (*off) += m->u.match_size;
+       return 0;
+}
+
+static inline int
+check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
+           unsigned int user_offset, int *was_return,
+           TC_HANDLE_T h)
+{
+       unsigned int toff;
+       STRUCT_STANDARD_TARGET *t;
+
+       assert(e->target_offset >= sizeof(STRUCT_ENTRY));
+       assert(e->next_offset >= e->target_offset
+              + sizeof(STRUCT_ENTRY_TARGET));
+       toff = sizeof(STRUCT_ENTRY);
+       IPT_MATCH_ITERATE(e, check_match, &toff);
+
+       assert(toff == e->target_offset);
+
+       t = (STRUCT_STANDARD_TARGET *)
+               GET_TARGET((STRUCT_ENTRY *)e);
+       /* next_offset will have to be multiple of entry alignment. */
+       assert(e->next_offset == ALIGN(e->next_offset));
+       assert(e->target_offset == ALIGN(e->target_offset));
+       assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
+       assert(!TC_IS_CHAIN(t->target.u.user.name, h));
+
+       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
+               assert(t->target.u.target_size
+                      == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
+
+               assert(t->verdict == -NF_DROP-1
+                      || t->verdict == -NF_ACCEPT-1
+                      || t->verdict == RETURN
+                      || t->verdict < (int)h->entries.size);
+
+               if (t->verdict >= 0) {
+                       STRUCT_ENTRY *te = get_entry(h, t->verdict);
+                       int idx;
+
+                       idx = 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
+                              || strcmp(GET_TARGET(index2entry(h, idx-1))
+                                        ->u.user.name, IPT_ERROR_TARGET)
+                              == 0);
+               }
+
+               if (t->verdict == RETURN
+                   && unconditional(&e->ip)
+                   && e->target_offset == sizeof(*e))
+                       *was_return = 1;
+               else
+                       *was_return = 0;
+       } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) {
+               assert(t->target.u.target_size
+                      == ALIGN(sizeof(struct ipt_error_target)));
+
+               /* If this is in user area, previous must have been return */
+               if (*off > user_offset)
+                       assert(*was_return);
+
+               *was_return = 0;
+       }
+       else *was_return = 0;
+
+       if (*off == user_offset)
+               assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0);
+
+       (*off) += e->next_offset;
+       (*i)++;
+       return 0;
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP_LOCAL_IN
+                          | 1 << NF_IP_FORWARD
+                          | 1 << NF_IP_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_POST_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_IN
+                           | 1 << NF_IP_POST_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)));
+
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)) || 
+                      (h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_IN
+                           | 1 << NF_IP_FORWARD
+                           | 1 << NF_IP_LOCAL_OUT
+                           | 1 << NF_IP_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
+               }
+
+#ifdef NF_IP_DROPPING
+       } else if (strcmp(h->info.name, "drop") == 0) {
+               assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
+
+               /* Hook should be first */
+               assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
+               user_offset = 0;
+#endif
+       } else {
+               fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ip));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               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]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc2/libip6tc.c b/libiptc2/libip6tc.c
new file mode 100644 (file)
index 0000000..9a78a5a
--- /dev/null
@@ -0,0 +1,449 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING       NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP6_LOCAL_IN
+#define HOOK_FORWARD           NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET    struct ip6t_entry_target
+#define STRUCT_ENTRY           struct ip6t_entry
+#define STRUCT_ENTRY_MATCH     struct ip6t_entry_match
+#define STRUCT_GETINFO         struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES     struct ip6t_get_entries
+#define STRUCT_COUNTERS                struct ip6t_counters
+#define STRUCT_COUNTERS_INFO   struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE         struct ip6t_replace
+
+#define STRUCT_TC_HANDLE       struct ip6tc_handle
+#define TC_HANDLE_T            ip6tc_handle_t
+
+#define ENTRY_ITERATE          IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ip6t_get_target
+
+#define ERROR_TARGET           IP6T_ERROR_TARGET
+#define NUMHOOKS               NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL         ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries6
+#define TC_IS_CHAIN            ip6tc_is_chain
+#define TC_FIRST_CHAIN         ip6tc_first_chain
+#define TC_NEXT_CHAIN          ip6tc_next_chain
+#define TC_FIRST_RULE          ip6tc_first_rule
+#define TC_NEXT_RULE           ip6tc_next_rule
+#define TC_GET_TARGET          ip6tc_get_target
+#define TC_BUILTIN             ip6tc_builtin
+#define TC_GET_POLICY          ip6tc_get_policy
+#define TC_INSERT_ENTRY                ip6tc_insert_entry
+#define TC_REPLACE_ENTRY       ip6tc_replace_entry
+#define TC_APPEND_ENTRY                ip6tc_append_entry
+#define TC_DELETE_ENTRY                ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY    ip6tc_delete_num_entry
+#define TC_CHECK_PACKET                ip6tc_check_packet
+#define TC_FLUSH_ENTRIES       ip6tc_flush_entries
+#define TC_ZERO_ENTRIES                ip6tc_zero_entries
+#define TC_ZERO_COUNTER                ip6tc_zero_counter
+#define TC_READ_COUNTER                ip6tc_read_counter
+#define TC_SET_COUNTER         ip6tc_set_counter
+#define TC_CREATE_CHAIN                ip6tc_create_chain
+#define TC_GET_REFERENCES      ip6tc_get_references
+#define TC_DELETE_CHAIN                ip6tc_delete_chain
+#define TC_RENAME_CHAIN                ip6tc_rename_chain
+#define TC_SET_POLICY          ip6tc_set_policy
+#define TC_GET_RAW_SOCKET      ip6tc_get_raw_socket
+#define TC_INIT                        ip6tc_init
+#define TC_FREE                        ip6tc_free
+#define TC_COMMIT              ip6tc_commit
+#define TC_STRERROR            ip6tc_strerror
+
+#define TC_AF                  AF_INET6
+#define TC_IPPROTO             IPPROTO_IPV6
+
+#define SO_SET_REPLACE         IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES         IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION         IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET                IP6T_STANDARD_TARGET
+#define LABEL_RETURN           IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT           IP6TC_LABEL_ACCEPT
+#define LABEL_DROP             IP6TC_LABEL_DROP
+#define LABEL_QUEUE            IP6TC_LABEL_QUEUE
+
+#define ALIGN                  IP6T_ALIGN
+#define RETURN                 IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+       int l, i;
+       for (l = 0; l < 128; l++) {
+               if (BIT6(a, l) == 0)
+                       break;
+       }
+       for (i = l + 1; i < 128; i++) {
+               if (BIT6(a, i) == 1)
+                       return -1;
+       }
+       return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+       size_t i;
+       char buf[40];
+       int len;
+       struct ip6t_entry_target *t;
+       
+       printf("Entry %u (%lu):\n", entry2index(handle, e),
+              entry2offset(handle, e));
+       puts("SRC IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.smsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       puts("DST IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.dmsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       printf("Interface: `%s'/", e->ipv6.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ipv6.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ipv6.proto);
+       if (e->ipv6.flags & IP6T_F_TOS)
+               printf("TOS: %u\n", e->ipv6.tos);
+       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);
+       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);
+
+       t = ip6t_get_target(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == IP6T_RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static int
+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. */
+       if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+           || a->ipv6.proto != b->ipv6.proto
+           || a->ipv6.tos != b->ipv6.tos
+           || a->ipv6.flags != b->ipv6.flags
+           || a->ipv6.invflags != b->ipv6.invflags)
+               return 0;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+                       return 0;
+               if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+                   != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+                       return 0;
+               if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+                       return 0;
+               if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+                   != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+                       return 0;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return 0;
+
+       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 1;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ipv6); i++)
+               if (((char *)ipv6)[i])
+                       break;
+
+       return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP6_LOCAL_IN
+                          | 1 << NF_IP6_FORWARD
+                          | 1 << NF_IP6_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_FORWARD
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+               }
+       } else {
+                fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ipv6));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               printf("target_size=%u, align=%u\n",
+                       t->target.u.target_size, ALIGN(sizeof(*t)));
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               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]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+
+#if 0
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc2/libiptc.c b/libiptc2/libiptc.c
new file mode 100644 (file)
index 0000000..3c3c76a
--- /dev/null
@@ -0,0 +1,1830 @@
+/* Library which manipulates firewall rules.  Version $Revision: 1.41 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (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>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ *     - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ *     - speed 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.  
+ * 2003-Jun-30: Harald Welte <laforge@netfilter.org>:
+ *     - reimplementation from scratch. *sigh*.  I hope nobody has to touch 
+ *       this code ever again.
+ */
+#include "linux_listhelp.h"
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+static int sockfd = -1;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [HOOK_PRE_ROUTING]  "PREROUTING",
+    [HOOK_LOCAL_IN]     "INPUT",
+    [HOOK_FORWARD]      "FORWARD",
+    [HOOK_LOCAL_OUT]    "OUTPUT",
+    [HOOK_POST_ROUTING] "POSTROUTING",
+#ifdef HOOK_DROPPING
+    [HOOK_DROPPING]    "DROPPING"
+#endif
+};
+
+struct counter_map
+{
+       enum {
+               COUNTER_MAP_NOMAP,
+               COUNTER_MAP_NORMAL_MAP,
+               COUNTER_MAP_ZEROED,
+               COUNTER_MAP_SET
+       } maptype;
+       unsigned int mappos;
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct rule_head
+{
+       struct list_head list;          /* list of rules in chain */
+       
+       struct chain_head *chain;       /* we're part of this chain */
+
+       struct chain_head *jumpto;      /* target of this rule, in case
+                                          it is a jump rule */
+
+       struct counter_map counter_map;
+
+       unsigned int size;              /* size of rule */
+       STRUCT_ENTRY *entry_blob;       /* pointer to entry in blob */
+       STRUCT_ENTRY entry[0];
+};
+
+struct chain_head
+{
+       struct list_head list;
+
+       char name[TABLE_MAXNAMELEN];
+       unsigned int hooknum;
+       struct list_head rules;
+       struct rule_head *firstrule;    /* first (ERROR) rule */
+       struct rule_head *lastrule;     /* last (RETURN) rule */
+};
+
+STRUCT_TC_HANDLE
+{
+       /* Have changes been made? */
+       int changed;
+
+       /* linked list of chains in this table */
+       struct list_head chains;
+       
+       /* current position of first_chain() / next_chain() */
+       struct chain_head *chain_iterator_cur;
+
+       /* current position of first_rule() / next_rule() */
+       struct rule_head *rule_iterator_cur;
+
+       /* the structure we receive from getsockopt() */
+       STRUCT_GETINFO info;
+
+       /* Array of hook names */
+       const char **hooknames;
+#if 0
+       /* Size in here reflects original state. */
+
+
+       /* Cached position of chain heads (NULL = no cache). */
+       unsigned int cache_num_chains;
+       unsigned int cache_num_builtins;
+       struct chain_cache *cache_chain_heads;
+
+       /* Chain iterator: current chain cache entry. */
+       struct chain_cache *cache_chain_iteration;
+
+       /* Rule iterator: terminal rule */
+       STRUCT_ENTRY *cache_rule_end;
+
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+#endif
+       STRUCT_GET_ENTRIES entries;
+};
+
+static void
+set_changed(TC_HANDLE_T h)
+{
+       h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+static struct rule_head *ruleh_alloc(unsigned int size)
+{
+       struct rule_head *ruleh = malloc(sizeof(*ruleh)+size);
+       if (!ruleh)
+               return NULL;
+       
+       memset(ruleh, 0, sizeof(*ruleh)+size);
+       ruleh->size = size;
+
+       return ruleh;
+}
+
+static void ruleh_free(struct rule_head *ruleh)
+{
+       list_del(&ruleh->list);
+       free(ruleh);
+}
+
+static struct chain_head *chainh_alloc(TC_HANDLE_T h, const char *name)
+{
+       struct chain_head *chainh = malloc(sizeof(*chainh));
+       if (!chainh)
+               return NULL;
+
+       memset(chainh, 0, sizeof(*chainh));
+       strncpy(chainh->name, name, sizeof(&chainh->name));
+       list_append(&chainh->list, &h->chains);
+
+       return chainh;
+}
+
+static void
+chainh_clean(struct chain_head *chainh)
+{
+       /* FIXME */
+       struct list_head *cur_item, *item2;
+
+       list_for_each_safe(cur_item, item2, &chainh->rules) {
+               struct rule_head *ruleh = list_entry(cur_item, 
+                                                    struct rule_head,
+                                                   list);
+               ruleh_free(ruleh);
+       }
+}
+
+static void 
+chainh_free(struct chain_head *chainh)
+{
+       chainh_clean(chainh);
+       list_del(&chainh->list);
+}
+
+static struct chain_head *
+chainh_find(TC_HANDLE_T h, const IPT_CHAINLABEL name)
+{
+       struct list_head *cur;
+
+       list_for_each(cur, &h->chains) {
+               struct chain_head *ch = list_entry(cur, struct chain_head, 
+                                                  list);
+               if (!strcmp(name, ch->name))
+                       return ch;
+       }
+       return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+       return chainh_find(handle, name);
+}
+
+
+/* 
+ * functions that directly operate on the blob 
+ */
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+/* needed by entry2index */
+static inline int
+get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       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,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static STRUCT_ENTRY *
+index2entry(TC_HANDLE_T h, unsigned int index)
+{
+       unsigned int pos = 0;
+       STRUCT_ENTRY *ret = NULL;
+
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     get_entry_n, index, &pos, &ret);
+
+       return ret;
+}
+
+static inline unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+       return entry2offset(h, index2entry(h, index));
+}
+
+static char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+       STRUCT_ENTRY *e;
+
+       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();
+       }
+
+       return (char *)GET_TARGET(e)->data;
+}
+
+#if 0
+static inline STRUCT_ENTRY *
+offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+}
+
+static inline unsigned int
+offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return entry2index(h, offset2entry(h, offset));
+}
+
+
+#endif
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_tc_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);
+
+       if ((h = malloc(len)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       h->changed = 0;
+
+       strcpy(h->info.name, tablename);
+       strcpy(h->entries.name, tablename);
+       INIT_LIST_HEAD(&h->chains);
+
+       return h;
+}
+
+/* get the name of the chain that we jump to */
+static char *
+parse_jumptarget(const STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+       STRUCT_ENTRY *jumpto;
+       int spos, labelidx;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0) {
+               /* called for non-standard target */
+               return "__FIXME";
+       }
+       /* Standard target: evaluate */
+       spos = *(int *)GET_TARGET(e)->data;
+       if (spos < 0) {
+               return "__FIXME";
+       }
+
+       jumpto = get_entry(h, spos);
+
+       /* Fall through rule */
+       if (jumpto == (void *)e + e->next_offset)
+               return "";
+
+       /* Must point to head of a chain: ie. after error rule */
+       /* FIXME: this needs to deal with internal jump targets */
+       labelidx = entry2index(h, jumpto) - 1;
+       return get_errorlabel(h, index2offset(h, labelidx));
+}
+
+/* parser functions */
+
+struct rule_head *
+append_entrycopy(const STRUCT_ENTRY *e, struct rule_head *prev)
+{
+       struct rule_head *ruleh = ruleh_alloc(e->next_offset);
+       if (!ruleh)
+               return NULL;
+       
+       memcpy(&ruleh->entry, e, e->next_offset);
+       ruleh->chain = prev->chain;
+       ruleh->entry_blob = e;
+       list_append(&ruleh->list, &prev->list);
+
+       return ruleh;
+}
+
+/* have to return 0 on success, bcf ENTRY_ITERATE */
+static inline int 
+parse_entry(const STRUCT_ENTRY *e, TC_HANDLE_T h, struct chain_head **curchain)
+{
+       int i;
+       union tgt_u {
+               STRUCT_ENTRY_TARGET ent;
+               STRUCT_STANDARD_TARGET std;
+               struct ipt_error_target err;
+       } *tgt;
+
+       struct rule_head *lastrule = list_entry((*curchain)->rules.prev,
+                                                struct rule_head, list);
+       struct rule_head *newrule;
+
+       tgt = (union tgt_u *) GET_TARGET(e);
+
+       if (e->target_offset == sizeof(STRUCT_ENTRY)
+           && (strcmp(tgt->ent.u.user.name, IPT_STANDARD_TARGET) == 0)) {
+               /* jump to somewhere else */
+               char *targname;
+               struct chain_head *chainh;
+
+               newrule = append_entrycopy(e, lastrule);
+
+               targname = parse_jumptarget(e, h);
+               if (!(chainh = find_label(targname, h))) {
+                       chainh = chainh_alloc(h, targname);
+               }
+               if (!chainh) {
+                       errno = ENOMEM;
+                       return 1;
+               }
+               newrule->jumpto = chainh;
+
+       } else if (e->target_offset == sizeof(STRUCT_ENTRY)
+                  && e->next_offset == sizeof(STRUCT_ENTRY)
+                                       + ALIGN(sizeof(struct ipt_error_target))
+                  && !strcmp(tgt->ent.u.user.name, ERROR_TARGET)) {
+               /* chain head */
+               *curchain = chainh_find(h, tgt->err.error);
+               if (!(*curchain)) {
+                       *curchain = chainh_alloc(h, tgt->err.error);
+                       /* FIXME: error handling */
+               }
+               newrule = append_entrycopy(e, lastrule);
+               (*curchain)->firstrule = newrule;
+
+       } else if (e->target_offset == sizeof(STRUCT_ENTRY)
+                  && e->next_offset == sizeof(STRUCT_ENTRY)
+                                       + ALIGN(sizeof(STRUCT_STANDARD_TARGET))
+                  && tgt->std.verdict == RETURN) {
+               /* chain end */
+               newrule = append_entrycopy(e, lastrule);
+               (*curchain)->lastrule = newrule;
+               *curchain = NULL;
+       } else {
+               /* normal rule */
+               newrule = append_entrycopy(e, lastrule);
+       }
+
+       /* create counter map entry */
+       newrule->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+       newrule->counter_map.mappos = entry2index(h, e);
+
+       /* iterate over hook_entries, needed to connect builtin
+        * chains with hook numbers */
+       for (i = 0; i < NUMHOOKS; i++) {
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               if (h->info.hook_entry[i] == entry2offset(h, e)) {
+                       /* found hook entry point */
+                       if (*curchain)
+                               (*curchain)->hooknum = i;
+               }
+               if (h->info.underflow[i] == entry2offset(h, e)) {
+                       /* found underflow point */
+               }
+       }
+
+       return 0;
+}
+
+static int parse_ruleset(TC_HANDLE_T h)
+{
+       struct chain_head *curchain;
+       
+       /* iterate over ruleset; create linked list of rule_head/chain_head */
+       if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size, 
+                     parse_entry, h, &curchain)) {
+               /* some error happened while iterating */
+               return 0;
+       }
+
+       return 1;
+}
+
+TC_HANDLE_T
+TC_INIT(const char *tablename)
+{
+       TC_HANDLE_T h;
+       STRUCT_GETINFO info;
+       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;
+
+       s = sizeof(info);
+
+       strcpy(info.name, tablename);
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+               return NULL;
+
+       if ((h = alloc_tc_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;
+               return NULL;
+       }
+#else
+       h->hooknames = hooknames;
+#endif
+
+       /* Initialize current state */
+       h->info = info;
+       //h->new_number = h->info.num_entries;
+       //
+       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;
+       }
+
+       CHECK(h);
+       parse_ruleset(h);
+
+       return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+       struct list_head *cur_item, *item2;
+
+       close(sockfd);
+       sockfd = -1;
+
+       /* free all chains */
+       list_for_each_safe(cur_item, item2, &(*h)->chains) {
+               struct chain_head *chead = list_entry(cur_item,
+                                                     struct chain_head,
+                                                     list);
+               chainh_free(chead);
+       }
+
+       /* FIXME: free all other ressources we might be using */
+
+       free(*h);
+       *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+       printf("Match name: `%s'\n", m->u.user.name);
+       return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+#if 0
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+       CHECK(handle);
+
+       printf("libiptc v%s.  %u entries, %u bytes.\n",
+              IPTABLES_VERSION,
+              handle->new_number, 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],
+              handle->info.hook_entry[HOOK_LOCAL_IN],
+              handle->info.hook_entry[HOOK_FORWARD],
+              handle->info.hook_entry[HOOK_LOCAL_OUT],
+              handle->info.hook_entry[HOOK_POST_ROUTING]);
+       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.underflow[HOOK_PRE_ROUTING],
+              handle->info.underflow[HOOK_LOCAL_IN],
+              handle->info.underflow[HOOK_FORWARD],
+              handle->info.underflow[HOOK_LOCAL_OUT],
+              handle->info.underflow[HOOK_POST_ROUTING]);
+
+       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 int alphasort(const void *a, const void *b)
+{
+       return strcmp(((struct chain_cache *)a)->name,
+                     ((struct chain_cache *)b)->name);
+}
+#endif
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+       return find_label(chain, handle) != NULL;
+}
+
+#if 0
+/* Returns the position of the final (ie. unconditional) element. */
+static unsigned int
+get_chain_end(const TC_HANDLE_T handle, unsigned int start)
+{
+       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);
+
+               /* 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();
+}
+#endif
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+       struct chain_head *firsthead = list_entry((*handle)->chains.next,
+                                                  struct chain_head, list);
+       (*handle)->chain_iterator_cur = firsthead;
+
+       return firsthead->name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+       struct chain_head *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
+       (*handle)->chain_iterator_cur = next;
+
+       if (&next->list == &(*handle)->chains)
+               return NULL;
+
+       return next->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_head *c;
+       struct rule_head *r;
+
+       c = find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* Empty chain: single return/policy rule */
+       if (list_empty(&c->rules))
+               return NULL;
+
+       r = list_entry(c->rules.next, struct rule_head, list);
+       (*handle)->rule_iterator_cur = r;
+
+       return r->entry;
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+       struct rule_head *r = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
+
+       if (&r->list == &r->chain->rules)
+               return NULL;
+
+       /* NOTE: prev is without any influence ! */
+       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;
+
+       CHECK(*handle);
+       if (!find_label(&off, chain, *handle)) {
+               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);
+}
+
+/* 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;
+
+       CHECK(*handle);
+       if (!find_label(&pos, chain, *handle)) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+       return index2entry(*handle, chainindex + n);
+}
+#endif
+
+static const char *
+target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+{
+       int spos;
+
+       /* 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)
+                       return LABEL_RETURN;
+               else if (spos == -NF_ACCEPT-1)
+                       return LABEL_ACCEPT;
+               else if (spos == -NF_DROP-1)
+                       return LABEL_DROP;
+               else if (spos == -NF_QUEUE-1)
+                       return LABEL_QUEUE;
+
+               fprintf(stderr, "ERROR: entry %p not a valid target (%d)\n",
+                       e, spos);
+               abort();
+       }
+
+#if 0
+//     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 */
+       /* FIXME: this needs to deal with internal jump targets */
+       labelidx = entry2index(handle, jumpto) - 1;
+       return get_errorlabel(handle, index2offset(handle, labelidx));
+#endif
+       return "";
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+                         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)
+{
+       STRUCT_ENTRY *e;
+       struct chain_head *chainh;
+       struct rule_head *ruleh;
+       int hook;
+
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0)
+               return NULL;
+
+       chainh = find_label(chain, *handle);
+       if (!chainh) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       ruleh = chainh->lastrule;
+
+       e = ruleh->entry;
+       *counters = e->counters;
+
+       return target_name(*handle, e);
+}
+
+#if 0
+static 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;
+
+       /* 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;
+       }
+
+       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;
+}
+#endif
+
+
+
+static int
+standard_map(STRUCT_ENTRY *e, int verdict)
+{
+       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 0;
+       }
+       /* memset for memcmp convenience on delete/replace */
+       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+       strcpy(t->target.u.user.name, STANDARD_TARGET);
+       t->verdict = verdict;
+
+       return 1;
+}
+
+static int
+map_target(const TC_HANDLE_T handle,
+          STRUCT_ENTRY *e,
+          unsigned int offset,
+          STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = (STRUCT_ENTRY_TARGET *)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);
+       /* Maybe it's a standard target name... */
+       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+               return standard_map(e, -NF_ACCEPT - 1);
+       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+               return standard_map(e, -NF_DROP - 1);
+       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+               return standard_map(e, -NF_QUEUE - 1);
+       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+               return standard_map(e, 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_head *c;
+
+#if 0
+               /* FIXME */
+               c = find_label(t->u.user.name, handle);
+               if (c)
+                       return standard_map(e, c->start_off);
+#endif
+       }
+
+       /* Must be a module?  If not, kernel will reject... */
+       /* memset to all 0 for your memcmp convenience. */
+       memset(t->u.user.name + strlen(t->u.user.name),
+              0,
+              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+       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;
+}
+
+static struct rule_head *
+ruleh_get_n(struct chain_head *chead, int rulenum) 
+{
+       int i = 0;
+       struct list_head *list;
+
+       
+       list_for_each(list, &chead->rules) {
+               struct rule_head *rhead = list_entry(list, struct rule_head, 
+                                                       list);
+               i++;
+               if (i == rulenum)
+                       return rhead;
+       }
+       return NULL;
+}
+
+/* Insert the entry `e' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *prev;
+
+       iptc_fn = TC_INSERT_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       prev = ruleh_get_n(c, rulenum-1);
+       if (!prev) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       if (append_entrycopy(e, prev))
+               return 1;
+
+       return 0;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+                const STRUCT_ENTRY *e,
+                unsigned int rulenum,
+                TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *repl;
+
+       iptc_fn = TC_REPLACE_ENTRY;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       repl = ruleh_get_n(c, rulenum);
+       if (!repl) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       if (!append_entrycopy(e, repl)) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       ruleh_free(repl);
+       return 1;
+}
+
+/* Append entry `e' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *rhead;
+
+       iptc_fn = TC_APPEND_ENTRY;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       rhead = list_entry(c->rules.prev, struct rule_head, list);
+       if(append_entrycopy(e, rhead))
+               return 1;
+       
+       return 0;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+               const unsigned char *a_elems,
+               const unsigned char *b_elems,
+               unsigned char **maskptr)
+{
+       const STRUCT_ENTRY_MATCH *b;
+       unsigned int i;
+
+       /* Offset of b is the same as a. */
+       b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+       if (a->u.match_size != b->u.match_size)
+               return 1;
+
+       if (strcmp(a->u.user.name, b->u.user.name) != 0)
+               return 1;
+
+       *maskptr += ALIGN(sizeof(*a));
+
+       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+                       return 1;
+       *maskptr += i;
+       return 0;
+}
+
+static inline int
+target_different(const unsigned char *a_targdata,
+                const unsigned char *b_targdata,
+                unsigned int tdatasize,
+                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;
+
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a,
+       const STRUCT_ENTRY *b,
+       unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `origfw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct list_head *cur, *cur2;
+
+       iptc_fn = TC_DELETE_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       list_for_each_safe(cur, cur2, &c->rules) {
+               struct rule_head *rhead = list_entry(cur, struct rule_head, 
+                                                       list);
+               if (is_same(rhead->entry, origfw, matchmask)) {
+                       ruleh_free(rhead);
+                       return 1;
+               }
+       }
+
+       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)
+{
+       struct chain_head *chainh;
+       struct rule_head *rhead;
+
+       iptc_fn = TC_DELETE_NUM_ENTRY;
+
+       if (!(chainh = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       rhead = ruleh_get_n(chainh, rulenum);
+       if (!rhead) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       ruleh_free(rhead);
+
+       return 1;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+               STRUCT_ENTRY *entry,
+               TC_HANDLE_T *handle)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       struct list_head *cur, *cur2;
+       struct chain_head *chainh;
+
+       if (!(chainh = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       list_for_each_safe(cur, cur2, &chainh->rules) {
+               struct rule_head *ruleh = list_entry(cur, struct rule_head, 
+                                                       list);
+               /* don't free the entry and policy/return entries */
+               if (ruleh != chainh->firstrule && ruleh != chainh->lastrule)
+                       ruleh_free(ruleh);
+       }
+       return 1;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct list_head *cur;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       list_for_each(cur, c->rules.next) {
+               struct rule_head *r = list_entry(cur, struct rule_head, list);
+               if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+                       r->counter_map.maptype = COUNTER_MAP_ZEROED;
+       }
+       set_changed(*handle);
+
+       return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_READ_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle) )
+             || !(r = ruleh_get_n(c, rulenum))) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       return &r->entry->counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_head *c;
+       struct rule_head *r;
+       
+       iptc_fn = TC_ZERO_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))
+             || !(r = ruleh_get_n(c, rulenum))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+               r->counter_map.maptype = COUNTER_MAP_ZEROED;
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+int 
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+              unsigned int rulenum,
+              STRUCT_COUNTERS *counters,
+              TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_SET_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))
+             || !(r = ruleh_get_n(c, rulenum))) {
+               errno = ENOENT;
+               return 0;
+       }
+       
+       r->counter_map.maptype = COUNTER_MAP_SET;
+       memcpy(&r->entry->counters, counters, sizeof(STRUCT_COUNTERS));
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       int ret;
+       struct chainstart {
+               STRUCT_ENTRY head;
+               struct ipt_error_target name;
+       } *newc1;
+       struct chainend {
+               STRUCT_ENTRY ret;
+               STRUCT_STANDARD_TARGET target;
+       } *newc2;
+       struct rule_head *newr1, *newr2;
+       struct chain_head *chead;
+
+       iptc_fn = TC_CREATE_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(chain, *handle)
+           || strcmp(chain, LABEL_DROP) == 0
+           || strcmp(chain, LABEL_ACCEPT) == 0
+           || strcmp(chain, LABEL_QUEUE) == 0
+           || strcmp(chain, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       chead = chainh_alloc(*handle, chain);
+       if (!chead) {
+               errno = ENOMEM;
+               return 0;
+       }
+       
+       newr1 = ruleh_alloc(sizeof(*newc1));
+       if (!newr1) {
+               chainh_free(chead);
+               return 0;
+       }
+       newc1 = (struct chainstart *) newr1->entry;
+
+       newr2 = ruleh_alloc(sizeof(*newc2));
+       if (!newr2) {
+               chainh_free(chead);
+               ruleh_free(newr1);
+               return 0;
+       }
+       newc2 = (struct chainend *) newr2->entry;
+
+       newc1->head.target_offset = sizeof(STRUCT_ENTRY);
+       newc1->head.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc1->name.t.u.user.name, ERROR_TARGET);
+       newc1->name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc1->name.error, chain);
+
+       newc2->ret.target_offset = sizeof(STRUCT_ENTRY);
+       newc2->ret.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       strcpy(newc2->target.target.u.user.name, STANDARD_TARGET);
+       newc2->target.target.u.target_size
+               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       newc2->target.verdict = RETURN;
+
+       list_prepend(&newr1->list, &chead->rules);
+       chead->firstrule = newr1;
+       list_append(&newr2->list, &chead->rules);
+       chead->lastrule = newr2;
+
+       return 1;
+}
+
+#if 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);
+
+               if (t->verdict == offset)
+                       (*ref)++;
+       }
+
+       return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+                 TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       *ref = 0;
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     count_ref, c->start_off, ref);
+       return 1;
+}
+#endif
+
+static unsigned int
+count_rules(struct chain_head *chainh)
+{
+       unsigned int numrules = 0;
+       struct list_head *cur;
+
+       list_for_each(cur, &chainh->rules) {
+               numrules++;
+       }
+
+       if (numrules <=2)
+               return 0;
+       else
+               return numrules-2;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int references;
+       struct chain_head *chainh;
+
+#if 0
+       if (!TC_GET_REFERENCES(&references, chain, handle))
+               return 0;
+
+       iptc_fn = TC_DELETE_CHAIN;
+
+       if (TC_BUILTIN(chain, *handle)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (references > 0) {
+               errno = EMLINK;
+               return 0;
+       }
+#endif 
+
+       if (!(chainh = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!(count_rules(chainh) == 0)) {
+               errno = ENOTEMPTY;
+               return 0;
+       }
+
+       chainh_free(chainh);
+       return 1;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+                   const IPT_CHAINLABEL newname,
+                   TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *ruleh;
+       struct ipt_error_target *t;
+
+       iptc_fn = TC_RENAME_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(newname, *handle)
+           || strcmp(newname, LABEL_DROP) == 0
+           || strcmp(newname, LABEL_ACCEPT) == 0
+           || strcmp(newname, LABEL_QUEUE) == 0
+           || strcmp(newname, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (!(c = find_label(oldname, *handle))
+           || TC_BUILTIN(oldname, *handle)) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ruleh = list_entry(&c->rules.next, struct rule_head, list);
+
+       t = (struct ipt_error_target *)
+               GET_TARGET(ruleh->entry);
+
+       memset(t->error, 0, sizeof(t->error));
+       strcpy(t->error, newname);
+
+       return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+             const IPT_CHAINLABEL policy,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       int ctrindex;
+       unsigned int hook;
+       struct chain_head *chainh;
+       struct rule_head *policyrh;
+       STRUCT_ENTRY *e;
+       STRUCT_STANDARD_TARGET *t;
+
+       iptc_fn = TC_SET_POLICY;
+       /* Figure out which chain. */
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0) {
+               errno = ENOENT;
+               return 0;
+       } else
+               hook--;
+
+       if (!(chainh = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       policyrh = chainh->lastrule;
+       if (policyrh) {
+               printf("ERROR: Policy for `%s' non-existant", chain);
+               return 0;
+       }
+
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(policyrh->entry);
+
+       if (strcmp(policy, LABEL_ACCEPT) == 0)
+               t->verdict = -NF_ACCEPT - 1;
+       else if (strcmp(policy, LABEL_DROP) == 0)
+               t->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));
+
+               policyrh->counter_map.maptype = COUNTER_MAP_SET;
+
+       } else {
+               policyrh->counter_map.maptype = COUNTER_MAP_NOMAP;
+               policyrh->counter_map.mappos = 0;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+                 const STRUCT_COUNTERS *a,
+                 const STRUCT_COUNTERS *b)
+{
+       answer->pcnt = a->pcnt - b->pcnt;
+       answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+       /* Replace, then map back the counters. */
+       STRUCT_REPLACE *repl;
+       STRUCT_COUNTERS_INFO *newcounters;
+       unsigned int i;
+       size_t counterlen;
+
+       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);
+       if (!repl) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the old counters we will get from kernel */
+       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+                               * (*handle)->info.num_entries);
+       if (!repl->counters) {
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the counters we're going to put back, later. */
+       newcounters = malloc(counterlen);
+       if (!newcounters) {
+               free(repl->counters);
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       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_counters = (*handle)->info.num_entries;
+       repl->valid_hooks = (*handle)->info.valid_hooks;
+       memcpy(repl->entries, (*handle)->entries.entrytable,
+              (*handle)->entries.size);
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       /* 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));
+
+                       break;
+               }
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       {
+               /* Kernel will think that pointer should be 64-bits, and get
+                  padding.  So we accomodate here (assumption: alignment of
+                  `counters' is on 64-bit boundary). */
+               u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+               if ((unsigned long)&newcounters->counters % 8 != 0) {
+                       fprintf(stderr,
+                               "counters alignment incorrect! Mail rusty!\n");
+                       abort();
+               }
+               *kernptr = newcounters->counters;
+       }
+#endif /* KERNEL_64_USERSPACE_32 */
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+                      newcounters, counterlen) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       free(repl->counters);
+       free(repl);
+       free(newcounters);
+
+ finished:
+       TC_FREE(handle);
+       return 1;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+       return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+       unsigned int i;
+       struct table_struct {
+               void *fn;
+               int err;
+               const char *message;
+       } table [] =
+         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+           { TC_INIT, EINVAL, "Module is wrong version" },
+           { TC_INIT, ENOENT, 
+                   "Table does not exist (do you need to insmod?)" },
+           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+           { TC_DELETE_CHAIN, EMLINK,
+             "Can't delete chain with references left" },
+           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+           /* EINVAL for CHECK probably means bad interface. */
+           { TC_CHECK_PACKET, EINVAL,
+             "Bad arguments (does that interface exist?)" },
+           { TC_CHECK_PACKET, ENOSYS,
+             "Checking will most likely never get implemented" },
+           /* ENOENT for DELETE probably means no matching rule */
+           { TC_DELETE_ENTRY, ENOENT,
+             "Bad rule (does a matching rule exist in that chain?)" },
+           { TC_SET_POLICY, ENOENT,
+             "Bad built-in chain name" },
+           { TC_SET_POLICY, EINVAL,
+             "Bad policy name" },
+
+           { NULL, 0, "Incompatible with this kernel" },
+           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+           { NULL, ENOMEM, "Memory allocation problem" },
+           { NULL, ENOENT, "No chain/target/match by that name" },
+         };
+
+       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+               if ((!table[i].fn || table[i].fn == iptc_fn)
+                   && table[i].err == err)
+                       return table[i].message;
+       }
+
+       return strerror(err);
+}
diff --git a/libiptc2/libiptc.cvs.c b/libiptc2/libiptc.cvs.c
new file mode 100644 (file)
index 0000000..3f03593
--- /dev/null
@@ -0,0 +1,1920 @@
+/* Library which manipulates firewall rules.  Version $Revision: 1.40 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (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>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ *     - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ *     - 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.  
+ */
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef __OPTIMIZE__
+STRUCT_ENTRY_TARGET *
+GET_TARGET(STRUCT_ENTRY *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static int sockfd = -1;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [HOOK_PRE_ROUTING]  "PREROUTING",
+    [HOOK_LOCAL_IN]     "INPUT",
+    [HOOK_FORWARD]      "FORWARD",
+    [HOOK_LOCAL_OUT]    "OUTPUT",
+    [HOOK_POST_ROUTING] "POSTROUTING",
+#ifdef HOOK_DROPPING
+    [HOOK_DROPPING]    "DROPPING"
+#endif
+};
+
+struct counter_map
+{
+       enum {
+               COUNTER_MAP_NOMAP,
+               COUNTER_MAP_NORMAL_MAP,
+               COUNTER_MAP_ZEROED,
+               COUNTER_MAP_SET
+       } maptype;
+       unsigned int mappos;
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct chain_cache
+{
+       char name[TABLE_MAXNAMELEN];
+       /* This is the first rule in chain. */
+       unsigned int start_off;
+       /* Last rule in chain */
+       unsigned int end_off;
+};
+
+STRUCT_TC_HANDLE
+{
+       /* Have changes been made? */
+       int changed;
+       /* Size in here reflects original state. */
+       STRUCT_GETINFO info;
+
+       struct counter_map *counter_map;
+       /* Array of hook names */
+       const char **hooknames;
+
+       /* Cached position of chain heads (NULL = no cache). */
+       unsigned int cache_num_chains;
+       unsigned int cache_num_builtins;
+       struct chain_cache *cache_chain_heads;
+
+       /* Chain iterator: current chain cache entry. */
+       struct chain_cache *cache_chain_iteration;
+
+       /* Rule iterator: terminal rule */
+       STRUCT_ENTRY *cache_rule_end;
+
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+       STRUCT_GET_ENTRIES entries;
+};
+
+static void
+set_changed(TC_HANDLE_T h)
+{
+       h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+static inline int
+get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       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,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static STRUCT_ENTRY *
+index2entry(TC_HANDLE_T h, unsigned int index)
+{
+       unsigned int pos = 0;
+       STRUCT_ENTRY *ret = NULL;
+
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     get_entry_n, index, &pos, &ret);
+
+       return ret;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+       return entry2offset(h, index2entry(h, index));
+}
+
+static inline STRUCT_ENTRY *
+offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+}
+
+static inline unsigned int
+offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return entry2index(h, offset2entry(h, offset));
+}
+
+
+static const char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+       STRUCT_ENTRY *e;
+
+       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();
+       }
+
+       return (const char *)GET_TARGET(e)->data;
+}
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+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);
+
+       if ((h = malloc(len)) == NULL) {
+               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;
+       strcpy(h->info.name, tablename);
+       strcpy(h->entries.name, tablename);
+
+       return h;
+}
+
+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;
+
+       s = sizeof(info);
+
+       strcpy(info.name, tablename);
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+               return NULL;
+
+       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;
+               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;
+
+       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;
+       }
+
+       CHECK(h);
+       return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+       close(sockfd);
+       sockfd = -1;
+       if ((*h)->cache_chain_heads)
+               free((*h)->cache_chain_heads);
+       free(*h);
+       *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+       printf("Match name: `%s'\n", m->u.user.name);
+       return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+       CHECK(handle);
+
+       printf("libiptc v%s.  %u entries, %u bytes.\n",
+              IPTABLES_VERSION,
+              handle->new_number, 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],
+              handle->info.hook_entry[HOOK_LOCAL_IN],
+              handle->info.hook_entry[HOOK_FORWARD],
+              handle->info.hook_entry[HOOK_LOCAL_OUT],
+              handle->info.hook_entry[HOOK_POST_ROUTING]);
+       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.underflow[HOOK_PRE_ROUTING],
+              handle->info.underflow[HOOK_LOCAL_IN],
+              handle->info.underflow[HOOK_FORWARD],
+              handle->info.underflow[HOOK_LOCAL_OUT],
+              handle->info.underflow[HOOK_POST_ROUTING]);
+
+       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--;
+                               if (i+1 >= 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;
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+       return 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)
+{
+       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);
+
+               /* 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();
+}
+
+/* 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))
+               return NULL;
+
+       (*handle)->cache_chain_iteration
+               = &(*handle)->cache_chain_heads[0];
+
+       return (*handle)->cache_chain_iteration->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++;
+
+       if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
+           == (*handle)->cache_num_chains)
+               return NULL;
+
+       return (*handle)->cache_chain_iteration->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;
+
+       c = find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* Empty chain: single return/policy rule */
+       if (c->start_off == c->end_off)
+               return NULL;
+
+       (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
+       return offset2entry(*handle, c->start_off);
+}
+
+/* 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)
+               return NULL;
+
+       return (void *)prev + prev->next_offset;
+}
+
+#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;
+
+       CHECK(*handle);
+       if (!find_label(&off, chain, *handle)) {
+               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);
+}
+
+/* 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;
+
+       CHECK(*handle);
+       if (!find_label(&pos, chain, *handle)) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+       return index2entry(*handle, chainindex + n);
+}
+#endif
+
+static const char *
+target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+{
+       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)
+                       return LABEL_RETURN;
+               else if (spos == -NF_ACCEPT-1)
+                       return LABEL_ACCEPT;
+               else if (spos == -NF_DROP-1)
+                       return LABEL_DROP;
+               else if (spos == -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();
+       }
+
+       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));
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+                         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);
+}
+
+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;
+
+       /* 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;
+       }
+
+       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;
+}
+
+/* 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);
+               return 0;
+       }
+
+       free(*handle);
+       *handle = newh;
+
+       return set_verdict(offset, rules_size, handle);
+}
+
+static int
+delete_rules(unsigned int num_rules, unsigned int rules_size,
+            unsigned int offset, unsigned int num_rules_offset,
+            TC_HANDLE_T *handle)
+{
+       unsigned int i;
+
+       if (offset + rules_size > (*handle)->entries.size) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* 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();
+               }
+
+               /* 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();
+               }
+       }
+
+       /* 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;
+
+       /* Fix the chain cache */
+       if (!correct_cache(*handle, offset, -(int)rules_size))
+               return 0;
+
+       return set_verdict(offset, -(int)rules_size, handle);
+}
+
+static int
+standard_map(STRUCT_ENTRY *e, int verdict)
+{
+       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 0;
+       }
+       /* memset for memcmp convenience on delete/replace */
+       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+       strcpy(t->target.u.user.name, STANDARD_TARGET);
+       t->verdict = verdict;
+
+       return 1;
+}
+
+static int
+map_target(const TC_HANDLE_T handle,
+          STRUCT_ENTRY *e,
+          unsigned int offset,
+          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). */
+       *old = *t;
+
+       /* Maybe it's empty (=> fall through) */
+       if (strcmp(t->u.user.name, "") == 0)
+               return standard_map(e, offset + e->next_offset);
+       /* Maybe it's a standard target name... */
+       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+               return standard_map(e, -NF_ACCEPT - 1);
+       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+               return standard_map(e, -NF_DROP - 1);
+       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+               return standard_map(e, -NF_QUEUE - 1);
+       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+               return standard_map(e, 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);
+       }
+
+       /* Must be a module?  If not, kernel will reject... */
+       /* memset to all 0 for your memcmp convenience. */
+       memset(t->u.user.name + strlen(t->u.user.name),
+              0,
+              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+       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,
+               const STRUCT_ENTRY *e,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_INSERT_ENTRY;
+       if (!(c = 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)) {
+               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))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, rulenum == 0, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+                const STRUCT_ENTRY *e,
+                unsigned int rulenum,
+                TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_REPLACE_ENTRY;
+
+       if (!(c = 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)) {
+               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))
+               return 0;
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, 1, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+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;
+
+       iptc_fn = TC_APPEND_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e,
+                       c->end_off, &old))
+               return 0;
+
+       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;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+               const unsigned char *a_elems,
+               const unsigned char *b_elems,
+               unsigned char **maskptr)
+{
+       const STRUCT_ENTRY_MATCH *b;
+       unsigned int i;
+
+       /* Offset of b is the same as a. */
+       b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+       if (a->u.match_size != b->u.match_size)
+               return 1;
+
+       if (strcmp(a->u.user.name, b->u.user.name) != 0)
+               return 1;
+
+       *maskptr += ALIGN(sizeof(*a));
+
+       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+                       return 1;
+       *maskptr += i;
+       return 0;
+}
+
+static inline int
+target_different(const unsigned char *a_targdata,
+                const unsigned char *b_targdata,
+                unsigned int tdatasize,
+                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;
+
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a,
+       const STRUCT_ENTRY *b,
+       unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       unsigned int offset;
+       struct chain_cache *c;
+       STRUCT_ENTRY *e, *fw;
+
+       iptc_fn = TC_DELETE_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       fw = malloc(origfw->next_offset);
+       if (fw == NULL) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       for (offset = c->start_off; offset < c->end_off;
+            offset += e->next_offset) {
+               STRUCT_ENTRY_TARGET discard;
+
+               memcpy(fw, origfw, origfw->next_offset);
+
+               /* FIXME: handle this in is_same --RR */
+               if (!map_target(*handle, fw, offset, &discard)) {
+                       free(fw);
+                       return 0;
+               }
+               e = get_entry(*handle, offset);
+
+#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;
+               }
+       }
+
+       free(fw);
+       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;
+
+       iptc_fn = TC_DELETE_NUM_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       index = offset2index(*handle, c->start_off) + rulenum;
+
+       if (index >= offset2index(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, index);
+       if (e == NULL) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
+                          index, handle);
+       return ret;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+               STRUCT_ENTRY *entry,
+               TC_HANDLE_T *handle)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties 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;
+
+       iptc_fn = TC_FLUSH_ENTRIES;
+       if (!(c = 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;
+}
+
+/* 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;
+
+       if (!(c = 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;
+       }
+       set_changed(*handle);
+
+       return 1;
+}
+
+STRUCT_COUNTERS *
+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;
+
+       iptc_fn = TC_READ_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return NULL;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       return &e->counters;
+}
+
+int
+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;
+       
+       iptc_fn = TC_ZERO_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               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;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+int 
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+              unsigned int rulenum,
+              STRUCT_COUNTERS *counters,
+              TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+
+       iptc_fn = TC_SET_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       (*handle)->counter_map[chainindex + rulenum].maptype
+               = COUNTER_MAP_SET;
+
+       memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+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;
+
+       iptc_fn = TC_CREATE_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(chain, *handle)
+           || strcmp(chain, LABEL_DROP) == 0
+           || strcmp(chain, LABEL_ACCEPT) == 0
+           || strcmp(chain, LABEL_QUEUE) == 0
+           || strcmp(chain, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               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;
+}
+
+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);
+
+               if (t->verdict == offset)
+                       (*ref)++;
+       }
+
+       return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+                 TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       *ref = 0;
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     count_ref, c->start_off, ref);
+       return 1;
+}
+
+/* Deletes a 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;
+
+       iptc_fn = TC_DELETE_CHAIN;
+
+       if (TC_BUILTIN(chain, *handle)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (references > 0) {
+               errno = EMLINK;
+               return 0;
+       }
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (c->start_off != c->end_off) {
+               errno = ENOTEMPTY;
+               return 0;
+       }
+
+       /* Need label index: preceeds chain start */
+       labelidx = offset2index(*handle, c->start_off) - 1;
+       labeloff = index2offset(*handle, labelidx);
+
+       start = offset2entry(*handle, c->start_off);
+
+       ret = delete_rules(2,
+                          get_entry(*handle, labeloff)->next_offset
+                          + start->next_offset,
+                          labeloff, labelidx, handle);
+       return ret;
+}
+
+/* Renames a chain. */
+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;
+
+       iptc_fn = TC_RENAME_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(newname, *handle)
+           || strcmp(newname, LABEL_DROP) == 0
+           || strcmp(newname, LABEL_ACCEPT) == 0
+           || strcmp(newname, LABEL_QUEUE) == 0
+           || strcmp(newname, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (!(c = find_label(oldname, *handle))
+           || TC_BUILTIN(oldname, *handle)) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               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);
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+             const IPT_CHAINLABEL policy,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       unsigned int hook;
+       unsigned int policyoff, ctrindex;
+       STRUCT_ENTRY *e;
+       STRUCT_STANDARD_TARGET *t;
+
+       iptc_fn = TC_SET_POLICY;
+       /* Figure out which chain. */
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0) {
+               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]);
+               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;
+       else if (strcmp(policy, LABEL_DROP) == 0)
+               t->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;
+
+       } else {
+               (*handle)->counter_map[ctrindex]
+                       = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+                 const STRUCT_COUNTERS *a,
+                 const STRUCT_COUNTERS *b)
+{
+       answer->pcnt = a->pcnt - b->pcnt;
+       answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+       /* Replace, then map back the counters. */
+       STRUCT_REPLACE *repl;
+       STRUCT_COUNTERS_INFO *newcounters;
+       unsigned int i;
+       size_t counterlen;
+
+       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);
+       if (!repl) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the old counters we will get from kernel */
+       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+                               * (*handle)->info.num_entries);
+       if (!repl->counters) {
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the counters we're going to put back, later. */
+       newcounters = malloc(counterlen);
+       if (!newcounters) {
+               free(repl->counters);
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       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_counters = (*handle)->info.num_entries;
+       repl->valid_hooks = (*handle)->info.valid_hooks;
+       memcpy(repl->entries, (*handle)->entries.entrytable,
+              (*handle)->entries.size);
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       /* 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));
+
+                       break;
+               }
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       {
+               /* Kernel will think that pointer should be 64-bits, and get
+                  padding.  So we accomodate here (assumption: alignment of
+                  `counters' is on 64-bit boundary). */
+               u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+               if ((unsigned long)&newcounters->counters % 8 != 0) {
+                       fprintf(stderr,
+                               "counters alignment incorrect! Mail rusty!\n");
+                       abort();
+               }
+               *kernptr = newcounters->counters;
+       }
+#endif /* KERNEL_64_USERSPACE_32 */
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+                      newcounters, counterlen) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       free(repl->counters);
+       free(repl);
+       free(newcounters);
+
+ finished:
+       TC_FREE(handle);
+       return 1;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+       return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+       unsigned int i;
+       struct table_struct {
+               void *fn;
+               int err;
+               const char *message;
+       } table [] =
+         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+           { TC_INIT, EINVAL, "Module is wrong version" },
+           { TC_INIT, ENOENT, 
+                   "Table does not exist (do you need to insmod?)" },
+           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+           { TC_DELETE_CHAIN, EMLINK,
+             "Can't delete chain with references left" },
+           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+           /* EINVAL for CHECK probably means bad interface. */
+           { TC_CHECK_PACKET, EINVAL,
+             "Bad arguments (does that interface exist?)" },
+           { TC_CHECK_PACKET, ENOSYS,
+             "Checking will most likely never get implemented" },
+           /* ENOENT for DELETE probably means no matching rule */
+           { TC_DELETE_ENTRY, ENOENT,
+             "Bad rule (does a matching rule exist in that chain?)" },
+           { TC_SET_POLICY, ENOENT,
+             "Bad built-in chain name" },
+           { TC_SET_POLICY, EINVAL,
+             "Bad policy name" },
+
+           { NULL, 0, "Incompatible with this kernel" },
+           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+           { NULL, ENOMEM, "Memory allocation problem" },
+           { NULL, ENOENT, "No chain/target/match by that name" },
+         };
+
+       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+               if ((!table[i].fn || table[i].fn == iptc_fn)
+                   && table[i].err == err)
+                       return table[i].message;
+       }
+
+       return strerror(err);
+}
diff --git a/libiptc2/libiptc2.c b/libiptc2/libiptc2.c
new file mode 100644 (file)
index 0000000..a7d7915
--- /dev/null
@@ -0,0 +1,1758 @@
+/* Library which manipulates firewall rules.  Version $Revision: 1.39 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (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>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org:
+ *     - Reimplementation of chain cache to use offsets instead of entries
+ * 
+ */
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef __OPTIMIZE__
+STRUCT_ENTRY_TARGET *
+GET_TARGET(STRUCT_ENTRY *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static int sockfd = -1;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [HOOK_PRE_ROUTING]  "PREROUTING",
+    [HOOK_LOCAL_IN]     "INPUT",
+    [HOOK_FORWARD]      "FORWARD",
+    [HOOK_LOCAL_OUT]    "OUTPUT",
+    [HOOK_POST_ROUTING] "POSTROUTING",
+#ifdef HOOK_DROPPING
+    [HOOK_DROPPING]    "DROPPING"
+#endif
+};
+
+struct counter_map
+{
+       enum {
+               COUNTER_MAP_NOMAP,
+               COUNTER_MAP_NORMAL_MAP,
+               COUNTER_MAP_ZEROED,
+               COUNTER_MAP_SET
+       } maptype;
+       unsigned int mappos;
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct rule_head
+{
+       struct list_head list;
+       
+       struct chain_head *chain;
+
+       unsigned int size;
+       STRUCT_ENTRY entry[0];
+}
+
+struct chain_head
+{
+       struct list_head list;
+
+       char name[TABLE_MAXNAMELEN];
+       unsigned int hooknum;
+       struct list_head rules;
+};
+
+STRUCT_TC_HANDLE
+{
+       /* Have changes been made? */
+       int changed;
+
+       struct list_head chains;
+       
+       struct chain_head *chain_iterator_cur;
+
+#if 0
+       /* Size in here reflects original state. */
+       STRUCT_GETINFO info;
+
+       struct counter_map *counter_map;
+       /* Array of hook names */
+       const char **hooknames;
+
+       /* Cached position of chain heads (NULL = no cache). */
+       unsigned int cache_num_chains;
+       unsigned int cache_num_builtins;
+       struct chain_cache *cache_chain_heads;
+
+       /* Chain iterator: current chain cache entry. */
+       struct chain_cache *cache_chain_iteration;
+
+       /* Rule iterator: terminal rule */
+       STRUCT_ENTRY *cache_rule_end;
+
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+       STRUCT_GET_ENTRIES entries;
+#endif
+};
+
+static void
+set_changed(TC_HANDLE_T h)
+{
+       h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+static inline int
+get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       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,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static STRUCT_ENTRY *
+index2entry(TC_HANDLE_T h, unsigned int index)
+{
+       unsigned int pos = 0;
+       STRUCT_ENTRY *ret = NULL;
+
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     get_entry_n, index, &pos, &ret);
+
+       return ret;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+       return entry2offset(h, index2entry(h, index));
+}
+
+static inline STRUCT_ENTRY *
+offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+}
+
+static inline unsigned int
+offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return entry2index(h, offset2entry(h, offset));
+}
+
+
+static const char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+       STRUCT_ENTRY *e;
+
+       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();
+       }
+
+       return (const char *)GET_TARGET(e)->data;
+}
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+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);
+
+       if ((h = malloc(len)) == NULL) {
+               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;
+       strcpy(h->info.name, tablename);
+       strcpy(h->entries.name, tablename);
+
+       return h;
+}
+
+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;
+
+       s = sizeof(info);
+
+       strcpy(info.name, tablename);
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+               return NULL;
+
+       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;
+               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;
+
+       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;
+       }
+
+       CHECK(h);
+       return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+       close(sockfd);
+       sockfd = -1;
+       if ((*h)->cache_chain_heads)
+               free((*h)->cache_chain_heads);
+       free(*h);
+       *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+       printf("Match name: `%s'\n", m->u.user.name);
+       return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+       CHECK(handle);
+
+       printf("libiptc v%s.  %u entries, %u bytes.\n",
+              IPTABLES_VERSION,
+              handle->new_number, 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],
+              handle->info.hook_entry[HOOK_LOCAL_IN],
+              handle->info.hook_entry[HOOK_FORWARD],
+              handle->info.hook_entry[HOOK_LOCAL_OUT],
+              handle->info.hook_entry[HOOK_POST_ROUTING]);
+       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.underflow[HOOK_PRE_ROUTING],
+              handle->info.underflow[HOOK_LOCAL_IN],
+              handle->info.underflow[HOOK_FORWARD],
+              handle->info.underflow[HOOK_LOCAL_OUT],
+              handle->info.underflow[HOOK_POST_ROUTING]);
+
+       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 int alphasort(const void *a, const void *b)
+{
+       return strcmp(((struct chain_cache *)a)->name,
+                     ((struct chain_cache *)b)->name);
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+       struct list_head *pos;
+
+       if (!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;
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+       return 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)
+{
+       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);
+
+               /* 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();
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+       (*handle)->chain_iterator_cur = (*handle)->chains;
+
+       return (*handle)->chains.name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+       struct chain_head *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
+       (*handle)->chain_iterator_cur = next;
+
+       if (next == (*handle)->chains)
+               return NULL;
+
+       return next->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_head *c;
+       struct rule_head *r;
+
+       c = find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* Empty chain: single return/policy rule */
+       if (list_empty(c->rules))
+               return NULL;
+
+       r = list_entry(&c->rules.next, struct rule_head, list);
+       (*handle)->rule_iterator_cur = r;
+
+       return r->entry;
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+       struct rule_head *r = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
+
+       if (r == r->chain)
+               return NULL;
+
+       /* NOTE: prev is without any influence ! */
+       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;
+
+       CHECK(*handle);
+       if (!find_label(&off, chain, *handle)) {
+               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);
+}
+
+/* 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;
+
+       CHECK(*handle);
+       if (!find_label(&pos, chain, *handle)) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+       return index2entry(*handle, chainindex + n);
+}
+#endif
+
+static const char *
+target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+{
+       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)
+                       return LABEL_RETURN;
+               else if (spos == -NF_ACCEPT-1)
+                       return LABEL_ACCEPT;
+               else if (spos == -NF_DROP-1)
+                       return LABEL_DROP;
+               else if (spos == -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();
+       }
+
+       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));
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+                         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);
+}
+
+static 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;
+
+       /* 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;
+       }
+
+       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;
+}
+
+/* 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;
+
+       if ((*handle)->cache_chain_heads)
+               free((*handle)->cache_chain_heads);
+       free(*handle);
+       *handle = newh;
+
+       return set_verdict(offset, rules_size, handle);
+}
+
+static int
+delete_rules(unsigned int num_rules, unsigned int rules_size,
+            unsigned int offset, unsigned int num_rules_offset,
+            TC_HANDLE_T *handle)
+{
+       unsigned int i;
+
+       if (offset + rules_size > (*handle)->entries.size) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* 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();
+               }
+
+               /* 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();
+               }
+       }
+
+       /* 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;
+
+       return set_verdict(offset, -(int)rules_size, handle);
+}
+
+static int
+standard_map(STRUCT_ENTRY *e, int verdict)
+{
+       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 0;
+       }
+       /* memset for memcmp convenience on delete/replace */
+       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+       strcpy(t->target.u.user.name, STANDARD_TARGET);
+       t->verdict = verdict;
+
+       return 1;
+}
+
+static int
+map_target(const TC_HANDLE_T handle,
+          STRUCT_ENTRY *e,
+          unsigned int offset,
+          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). */
+       *old = *t;
+
+       /* Maybe it's empty (=> fall through) */
+       if (strcmp(t->u.user.name, "") == 0)
+               return standard_map(e, offset + e->next_offset);
+       /* Maybe it's a standard target name... */
+       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+               return standard_map(e, -NF_ACCEPT - 1);
+       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+               return standard_map(e, -NF_DROP - 1);
+       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+               return standard_map(e, -NF_QUEUE - 1);
+       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+               return standard_map(e, 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);
+       }
+
+       /* Must be a module?  If not, kernel will reject... */
+       /* memset to all 0 for your memcmp convenience. */
+       memset(t->u.user.name + strlen(t->u.user.name),
+              0,
+              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+       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,
+               const STRUCT_ENTRY *e,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_INSERT_ENTRY;
+       if (!(c = 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)) {
+               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))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, rulenum == 0, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+                const STRUCT_ENTRY *e,
+                unsigned int rulenum,
+                TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_REPLACE_ENTRY;
+
+       if (!(c = 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)) {
+               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))
+               return 0;
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, 1, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+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;
+
+       iptc_fn = TC_APPEND_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e,
+                       c->end_off, &old))
+               return 0;
+
+       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;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+               const unsigned char *a_elems,
+               const unsigned char *b_elems,
+               unsigned char **maskptr)
+{
+       const STRUCT_ENTRY_MATCH *b;
+       unsigned int i;
+
+       /* Offset of b is the same as a. */
+       b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+       if (a->u.match_size != b->u.match_size)
+               return 1;
+
+       if (strcmp(a->u.user.name, b->u.user.name) != 0)
+               return 1;
+
+       *maskptr += ALIGN(sizeof(*a));
+
+       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+                       return 1;
+       *maskptr += i;
+       return 0;
+}
+
+static inline int
+target_different(const unsigned char *a_targdata,
+                const unsigned char *b_targdata,
+                unsigned int tdatasize,
+                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;
+
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a,
+       const STRUCT_ENTRY *b,
+       unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       unsigned int offset;
+       struct chain_cache *c;
+       STRUCT_ENTRY *e, *fw;
+
+       iptc_fn = TC_DELETE_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       fw = malloc(origfw->next_offset);
+       if (fw == NULL) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       for (offset = c->start_off; offset < c->end_off;
+            offset += e->next_offset) {
+               STRUCT_ENTRY_TARGET discard;
+
+               memcpy(fw, origfw, origfw->next_offset);
+
+               /* FIXME: handle this in is_same --RR */
+               if (!map_target(*handle, fw, offset, &discard)) {
+                       free(fw);
+                       return 0;
+               }
+               e = get_entry(*handle, offset);
+
+#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;
+               }
+       }
+
+       free(fw);
+       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;
+
+       iptc_fn = TC_DELETE_NUM_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       index = offset2index(*handle, c->start_off) + rulenum;
+
+       if (index >= offset2index(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, index);
+       if (e == NULL) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
+                          index, handle);
+       return ret;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+               STRUCT_ENTRY *entry,
+               TC_HANDLE_T *handle)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties 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;
+
+       iptc_fn = TC_FLUSH_ENTRIES;
+       if (!(c = 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;
+}
+
+/* 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;
+
+       if (!(c = 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;
+       }
+       set_changed(*handle);
+
+       return 1;
+}
+
+STRUCT_COUNTERS *
+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;
+
+       iptc_fn = TC_READ_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return NULL;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       return &e->counters;
+}
+
+int
+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;
+       
+       iptc_fn = TC_ZERO_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               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;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+int 
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+              unsigned int rulenum,
+              STRUCT_COUNTERS *counters,
+              TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+
+       iptc_fn = TC_SET_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       (*handle)->counter_map[chainindex + rulenum].maptype
+               = COUNTER_MAP_SET;
+
+       memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+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;
+
+       iptc_fn = TC_CREATE_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(chain, *handle)
+           || strcmp(chain, LABEL_DROP) == 0
+           || strcmp(chain, LABEL_ACCEPT) == 0
+           || strcmp(chain, LABEL_QUEUE) == 0
+           || strcmp(chain, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               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;
+
+       /* Add just before terminal entry */
+       ret = insert_rules(2, sizeof(newc), &newc.head,
+                          index2offset(*handle, (*handle)->new_number - 1),
+                          (*handle)->new_number - 1,
+                          0, handle);
+       return ret;
+}
+
+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);
+
+               if (t->verdict == offset)
+                       (*ref)++;
+       }
+
+       return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+                 TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       *ref = 0;
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     count_ref, c->start_off, ref);
+       return 1;
+}
+
+/* Deletes a 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;
+
+       iptc_fn = TC_DELETE_CHAIN;
+
+       if (TC_BUILTIN(chain, *handle)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (references > 0) {
+               errno = EMLINK;
+               return 0;
+       }
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (c->start_off != c->end_off) {
+               errno = ENOTEMPTY;
+               return 0;
+       }
+
+       /* Need label index: preceeds chain start */
+       labelidx = offset2index(*handle, c->start_off) - 1;
+       labeloff = index2offset(*handle, labelidx);
+
+       start = offset2entry(*handle, c->start_off);
+
+       ret = delete_rules(2,
+                          get_entry(*handle, labeloff)->next_offset
+                          + start->next_offset,
+                          labeloff, labelidx, handle);
+       return ret;
+}
+
+/* Renames a chain. */
+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;
+
+       iptc_fn = TC_RENAME_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(newname, *handle)
+           || strcmp(newname, LABEL_DROP) == 0
+           || strcmp(newname, LABEL_ACCEPT) == 0
+           || strcmp(newname, LABEL_QUEUE) == 0
+           || strcmp(newname, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (!(c = find_label(oldname, *handle))
+           || TC_BUILTIN(oldname, *handle)) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               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);
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+             const IPT_CHAINLABEL policy,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       unsigned int hook;
+       unsigned int policyoff, ctrindex;
+       STRUCT_ENTRY *e;
+       STRUCT_STANDARD_TARGET *t;
+
+       iptc_fn = TC_SET_POLICY;
+       /* Figure out which chain. */
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0) {
+               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]);
+               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;
+       else if (strcmp(policy, LABEL_DROP) == 0)
+               t->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;
+
+       } else {
+               (*handle)->counter_map[ctrindex]
+                       = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+                 const STRUCT_COUNTERS *a,
+                 const STRUCT_COUNTERS *b)
+{
+       answer->pcnt = a->pcnt - b->pcnt;
+       answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+       /* Replace, then map back the counters. */
+       STRUCT_REPLACE *repl;
+       STRUCT_COUNTERS_INFO *newcounters;
+       unsigned int i;
+       size_t counterlen;
+
+       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);
+       if (!repl) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the old counters we will get from kernel */
+       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+                               * (*handle)->info.num_entries);
+       if (!repl->counters) {
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the counters we're going to put back, later. */
+       newcounters = malloc(counterlen);
+       if (!newcounters) {
+               free(repl->counters);
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       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_counters = (*handle)->info.num_entries;
+       repl->valid_hooks = (*handle)->info.valid_hooks;
+       memcpy(repl->entries, (*handle)->entries.entrytable,
+              (*handle)->entries.size);
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       /* 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));
+
+                       break;
+               }
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       {
+               /* Kernel will think that pointer should be 64-bits, and get
+                  padding.  So we accomodate here (assumption: alignment of
+                  `counters' is on 64-bit boundary). */
+               u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+               if ((unsigned long)&newcounters->counters % 8 != 0) {
+                       fprintf(stderr,
+                               "counters alignment incorrect! Mail rusty!\n");
+                       abort();
+               }
+               *kernptr = newcounters->counters;
+       }
+#endif /* KERNEL_64_USERSPACE_32 */
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+                      newcounters, counterlen) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       free(repl->counters);
+       free(repl);
+       free(newcounters);
+
+ finished:
+       TC_FREE(handle);
+       return 1;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+       return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+       unsigned int i;
+       struct table_struct {
+               void *fn;
+               int err;
+               const char *message;
+       } table [] =
+         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+           { TC_INIT, EINVAL, "Module is wrong version" },
+           { TC_INIT, ENOENT, 
+                   "Table does not exist (do you need to insmod?)" },
+           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+           { TC_DELETE_CHAIN, EMLINK,
+             "Can't delete chain with references left" },
+           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+           /* EINVAL for CHECK probably means bad interface. */
+           { TC_CHECK_PACKET, EINVAL,
+             "Bad arguments (does that interface exist?)" },
+           { TC_CHECK_PACKET, ENOSYS,
+             "Checking will most likely never get implemented" },
+           /* ENOENT for DELETE probably means no matching rule */
+           { TC_DELETE_ENTRY, ENOENT,
+             "Bad rule (does a matching rule exist in that chain?)" },
+           { TC_SET_POLICY, ENOENT,
+             "Bad built-in chain name" },
+           { TC_SET_POLICY, EINVAL,
+             "Bad policy name" },
+
+           { NULL, 0, "Incompatible with this kernel" },
+           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+           { NULL, ENOMEM, "Memory allocation problem" },
+           { NULL, ENOENT, "No chain/target/match by that name" },
+         };
+
+       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+               if ((!table[i].fn || table[i].fn == iptc_fn)
+                   && table[i].err == err)
+                       return table[i].message;
+       }
+
+       return strerror(err);
+}
diff --git a/libiptc2/linux_list.h b/libiptc2/linux_list.h
new file mode 100644 (file)
index 0000000..41de482
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef _LINUXLIST_H
+#define _LINUXLIST_H
+
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+}
+
+/**
+ * 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_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * 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)
+{
+       struct list_head *first = list->next;
+
+       if (first != list) {
+               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_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) \
+       ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->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; pos != (head); pos = pos->next)
+               
+/**
+ * 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)
+
+#endif
diff --git a/libiptc2/linux_listhelp.h b/libiptc2/linux_listhelp.h
new file mode 100644 (file)
index 0000000..466d921
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef _LINUX_LISTHELP_H
+#define _LINUX_LISTHELP_H
+#include <stdlib.h>
+#include <string.h>
+#include "linux_list.h"
+
+/* Header to do more comprehensive job than linux/list.h; assume list
+   is first entry in structure. */
+
+/* Return pointer to first true entry, if any, or NULL.  A macro
+   required to allow inlining of cmpfn. */
+#define LIST_FIND(head, cmpfn, type, args...)          \
+({                                                     \
+       const struct list_head *__i = (head);           \
+                                                       \
+       do {                                            \
+               __i = __i->next;                        \
+               if (__i == (head)) {                    \
+                       __i = NULL;                     \
+                       break;                          \
+               }                                       \
+       } while (!cmpfn((const type)__i , ## args));    \
+       (type)__i;                                      \
+})
+
+#define LIST_FIND_W(head, cmpfn, type, args...)        \
+({                                             \
+       const struct list_head *__i = (head);   \
+                                               \
+       do {                                    \
+               __i = __i->next;                \
+               if (__i == (head)) {            \
+                       __i = NULL;             \
+                       break;                  \
+               }                               \
+       } while (!cmpfn((type)__i , ## args));  \
+       (type)__i;                              \
+})
+
+static inline int
+__list_cmp_same(const void *p1, const void *p2) { return p1 == p2; }
+
+/* Is this entry in the list? */
+static inline int
+list_inlist(struct list_head *head, const void *entry)
+{
+       return LIST_FIND(head, __list_cmp_same, void *, entry) != NULL;
+}
+
+/* Delete from list. */
+#define LIST_DELETE(head, oldentry) list_del((struct list_head *)oldentry)
+
+/* Append. */
+static inline void
+list_append(struct list_head *head, void *new)
+{
+       list_add((new), (head)->prev);
+}
+
+/* Prepend. */
+static inline void
+list_prepend(struct list_head *head, void *new)
+{
+       list_add(new, head);
+}
+
+/* Insert according to ordering function; insert before first true. */
+#define LIST_INSERT(head, new, cmpfn)                          \
+do {                                                           \
+       struct list_head *__i;                                  \
+       for (__i = (head)->next;                                \
+            !cmpfn((new), (typeof (new))__i) && __i != (head); \
+            __i = __i->next);                                  \
+       list_add((struct list_head *)(new), __i->prev);         \
+} while(0)
+
+/* If the field after the list_head is a nul-terminated string, you
+   can use these functions. */
+static inline int __list_cmp_name(const void *i, const char *name)
+{
+       return strcmp(name, i+sizeof(struct list_head)) == 0;
+}
+
+/* Returns false if same name already in list, otherwise does insert. */
+static inline int
+list_named_insert(struct list_head *head, void *new)
+{
+       if (LIST_FIND(head, __list_cmp_name, void *,
+                     new + sizeof(struct list_head)))
+               return 0;
+       list_prepend(head, new);
+       return 1;
+}
+
+/* Find this named element in the list. */
+#define list_named_find(head, name)                    \
+LIST_FIND(head, __list_cmp_name, void *, name)
+
+#endif /*_LISTHELP_H*/