iptables-1.2.9-2.3.1.src.rpm
authorMark Huang <mlhuang@cs.princeton.edu>
Fri, 29 Oct 2004 15:06:42 +0000 (15:06 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Fri, 29 Oct 2004 15:06:42 +0000 (15:06 +0000)
31 files changed:
.#iptables.c.1.54 [new file with mode: 0644]
.#iptables.c.1.55 [new file with mode: 0644]
Makefile.nolibnsl [new file with mode: 0644]
extensions/.#libipt_ECN.c.1.7 [new file with mode: 0644]
extensions/.#libipt_recent.c.1.6 [new file with mode: 0644]
extensions/.addrtype-test [new file with mode: 0755]
extensions/.pool-test [new file with mode: 0755]
extensions/libipt_POOL.c [new file with mode: 0644]
extensions/libipt_icmp.c.print_type [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]
ip6tables.c.selinux [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-restore.c.counters [new file with mode: 0644]
iptables.8 [new file with mode: 0644]
iptables.c.selinux [new file with mode: 0644]
libipq/libipq.c.netlink [new file with mode: 0644]
libiptc/libip6tc.c.orig [new file with mode: 0644]
libiptc2/.#libiptc.c.1.40 [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/.#iptables.c.1.54 b/.#iptables.c.1.54
new file mode 100644 (file)
index 0000000..a838e56
--- /dev/null
@@ -0,0 +1,2298 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC    0x0001
+#define FMT_NOCOUNTS   0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS    0x0008
+#define FMT_NOTABLE    0x0010
+#define FMT_NOTARGET   0x0020
+#define FMT_VIA                0x0040
+#define FMT_NONEWLINE  0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                       | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE               0x0000U
+#define CMD_INSERT             0x0001U
+#define CMD_DELETE             0x0002U
+#define CMD_DELETE_NUM         0x0004U
+#define CMD_REPLACE            0x0008U
+#define CMD_APPEND             0x0010U
+#define CMD_LIST               0x0020U
+#define CMD_FLUSH              0x0040U
+#define CMD_ZERO               0x0080U
+#define CMD_NEW_CHAIN          0x0100U
+#define CMD_DELETE_CHAIN       0x0200U
+#define CMD_SET_POLICY         0x0400U
+#define CMD_CHECK              0x0800U
+#define CMD_RENAME_CHAIN       0x1000U
+#define NUMBER_OF_CMD  13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+                                'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE       0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_SOURCE     0x00002U
+#define OPT_DESTINATION        0x00004U
+#define OPT_PROTOCOL   0x00008U
+#define OPT_JUMP       0x00010U
+#define OPT_VERBOSE    0x00020U
+#define OPT_EXPANDED   0x00040U
+#define OPT_VIANAMEIN  0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_FRAGMENT    0x00200U
+#define OPT_LINENUMBERS 0x00400U
+#define OPT_COUNTERS   0x00800U
+#define NUMBER_OF_OPT  12
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'};
+
+static struct option original_opts[] = {
+       { "append", 1, 0, 'A' },
+       { "delete", 1, 0,  'D' },
+       { "insert", 1, 0,  'I' },
+       { "replace", 1, 0,  'R' },
+       { "list", 2, 0,  'L' },
+       { "flush", 2, 0,  'F' },
+       { "zero", 2, 0,  'Z' },
+       { "new-chain", 1, 0,  'N' },
+       { "delete-chain", 2, 0,  'X' },
+       { "rename-chain", 1, 0,  'E' },
+       { "policy", 1, 0,  'P' },
+       { "source", 1, 0, 's' },
+       { "destination", 1, 0,  'd' },
+       { "src", 1, 0,  's' }, /* synonym */
+       { "dst", 1, 0,  'd' }, /* synonym */
+       { "protocol", 1, 0,  'p' },
+       { "in-interface", 1, 0, 'i' },
+       { "jump", 1, 0, 'j' },
+       { "table", 1, 0, 't' },
+       { "match", 1, 0, 'm' },
+       { "numeric", 0, 0, 'n' },
+       { "out-interface", 1, 0, 'o' },
+       { "verbose", 0, 0, 'v' },
+       { "exact", 0, 0, 'x' },
+       { "fragments", 0, 0, 'f' },
+       { "version", 0, 0, 'V' },
+       { "help", 2, 0, 'h' },
+       { "line-numbers", 0, 0, '0' },
+       { "modprobe", 1, 0, 'M' },
+       { "set-counters", 1, 0, 'c' },
+       { 0 }
+};
+
+/* we need this for iptables-restore.  iptables-restore.c sets line to the
+ * current line of the input file, in order  to give a more precise error
+ * message.  iptables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+#ifndef __OPTIMIZE__
+struct ipt_entry_target *
+ipt_get_target(struct ipt_entry *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+       /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x','x',' '},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*CHECK*/     {'x','+','+','+','x',' ','x',' ',' ',' ','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IPT_INV_SRCIP,
+/* -d */ IPT_INV_DSTIP,
+/* -p */ IPT_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IPT_INV_VIA_IN,
+/* -o */ IPT_INV_VIA_OUT,
+/* -f */ IPT_INV_FRAG,
+/*--line*/ 0
+};
+
+const char *program_version;
+const char *program_name;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct iptables_match *iptables_matches = NULL;
+struct iptables_target *iptables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries(const iptc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmp", IPPROTO_ICMP },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+       unsigned int i;
+
+       if (proto && !nolookup) {
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent)
+                       return pent->p_name;
+       }
+
+       for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+               if (chain_protos[i].num == proto)
+                       return chain_protos[i].name;
+
+       return NULL;
+}
+
+struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+       static struct in_addr addr;
+       unsigned char *addrp;
+       char *p, *q;
+       unsigned int onebyte;
+       int i;
+       char buf[20];
+
+       /* copy dotted string, because we need to modify it */
+       strncpy(buf, dotted, sizeof(buf) - 1);
+       addrp = (unsigned char *) &(addr.s_addr);
+
+       p = buf;
+       for (i = 0; i < 3; i++) {
+               if ((q = strchr(p, '.')) == NULL)
+                       return (struct in_addr *) NULL;
+
+               *q = '\0';
+               if (string_to_number(p, 0, 255, &onebyte) == -1)
+                       return (struct in_addr *) NULL;
+
+               addrp[i] = (unsigned char) onebyte;
+               p = q + 1;
+       }
+
+       /* we've checked 3 bytes, now we check the last one */
+       if (string_to_number(p, 0, 255, &onebyte) == -1)
+               return (struct in_addr *) NULL;
+
+       addrp[3] = (unsigned char) onebyte;
+
+       return &addr;
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+       struct netent *net;
+       static struct in_addr addr;
+
+       if ((net = getnetbyname(name)) != NULL) {
+               if (net->n_addrtype != AF_INET)
+                       return (struct in_addr *) NULL;
+               addr.s_addr = htonl((unsigned long) net->n_net);
+               return &addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+       /* memcpy(dst, src, sizeof(struct in_addr)); */
+       dst->s_addr = src->s_addr;
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       if (status == PARAMETER_PROBLEM)
+               exit_tryhelp(status);
+       if (status == VERSION_PROBLEM)
+               fprintf(stderr,
+                       "Perhaps iptables or your kernel needs to be upgraded.\n");
+       exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+       if (line != -1)
+               fprintf(stderr, "Error occurred at line: %d\n", line);
+       fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+                       program_name, program_name );
+       exit(status);
+}
+
+void
+exit_printhelp(void)
+{
+       struct iptables_match *m = NULL;
+       struct iptables_target *t = NULL;
+
+       printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+              program_name, program_version, program_name, program_name,
+              program_name, program_name, program_name, program_name,
+              program_name, program_name);
+
+       printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain          Append to chain\n"
+"  --delete  -D chain          Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"                              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"                              Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"                              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]                List the rules in a chain or all chains\n"
+"  --flush   -F [chain]                Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]                Zero counters in chain or all chains\n"
+"  --new     -N chain          Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]                Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"                              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"                              Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --proto     -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask]\n"
+"                              source specification\n"
+"  --destination -d [!] address[/mask]\n"
+"                              destination specification\n"
+"  --in-interface -i [!] input name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --jump      -j target\n"
+"                              target for rule (may load target extension)\n"
+"  --match     -m match\n"
+"                              extended match (may load extension)\n"
+"  --numeric   -n              numeric output of addresses and ports\n"
+"  --out-interface -o [!] output name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --table     -t table        table to manipulate (default: `filter')\n"
+"  --verbose   -v              verbose mode\n"
+"  --line-numbers              print line numbers when listing\n"
+"  --exact     -x              expand numbers (display exact values)\n"
+"[!] --fragment        -f              match second or further fragments only\n"
+"  --modprobe=<command>                try to insert modules using this command\n"
+"  --set-counters PKTS BYTES   set the counter during insert/append\n"
+"[!] --version -V              print package version.\n");
+
+       /* Print out any special helps. A user might like to be able
+          to add a --help to the commandline, and see expected
+          results. So we call help for all matches & targets */
+       for (t=iptables_targets;t;t=t->next) {
+               printf("\n");
+               t->help();
+       }
+       for (m=iptables_matches;m;m=m->next) {
+               printf("\n");
+               m->help();
+       }
+       exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 0; j < NUMBER_OF_CMD; j++) {
+                       if (!(command & (1<<j)))
+                               continue;
+
+                       if (!(options & (1<<i))) {
+                               if (commands_v_options[j][i] == '+')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "You need to supply the `-%c' "
+                                                  "option for this command\n",
+                                                  optflags[i]);
+                       } else {
+                               if (commands_v_options[j][i] != 'x')
+                                       legal = 1;
+                               else if (legal == 0)
+                                       legal = -1;
+                       }
+               }
+               if (legal == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Illegal option `-%c' with this command\n",
+                                  optflags[i]);
+       }
+}
+
+static char
+opt2char(int option)
+{
+       const char *ptr;
+       for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+       const char *ptr;
+       for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static void
+add_command(int *cmd, const int newcmd, const int othercmds, int invert)
+{
+       if (invert)
+               exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+       if (*cmd & (~othercmds))
+               exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+                          cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+       *cmd |= newcmd;
+}
+
+int
+check_inverse(const char option[], int *invert, int *optind, int argc)
+{
+       if (option && strcmp(option, "!") == 0) {
+               if (*invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Multiple `!' flags not allowed");
+               *invert = TRUE;
+               if (optind) {
+                       *optind = *optind+1;
+                       if (argc && *optind > argc)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "no argument following `!'");
+               }
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+       void *p;
+
+       if ((p = calloc(count, size)) == NULL) {
+               perror("iptables: calloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+       void *p;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("iptables: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+       struct hostent *host;
+       struct in_addr *addr;
+       unsigned int i;
+
+       *naddr = 0;
+       if ((host = gethostbyname(name)) != NULL) {
+               if (host->h_addrtype != AF_INET ||
+                   host->h_length != sizeof(struct in_addr))
+                       return (struct in_addr *) NULL;
+
+               while (host->h_addr_list[*naddr] != (char *) NULL)
+                       (*naddr)++;
+               addr = fw_calloc(*naddr, sizeof(struct in_addr));
+               for (i = 0; i < *naddr; i++)
+                       inaddrcpy(&(addr[i]),
+                                 (struct in_addr *) host->h_addr_list[i]);
+               return addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+       struct hostent *host;
+
+       if ((host = gethostbyaddr((char *) addr,
+                                 sizeof(struct in_addr), AF_INET)) != NULL)
+               return (char *) host->h_name;
+
+       return (char *) NULL;
+}
+
+/*
+ *     All functions starting with "parse" should succeed, otherwise
+ *     the program fails.
+ *     Most routines return pointers to static data that may change
+ *     between calls to the same or other routines with a few exceptions:
+ *     "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *     return global static data.
+*/
+
+static struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+       struct in_addr *addrp, *addrptmp;
+
+       if ((addrptmp = dotted_to_addr(name)) != NULL ||
+           (addrptmp = network_to_addr(name)) != NULL) {
+               addrp = fw_malloc(sizeof(struct in_addr));
+               inaddrcpy(addrp, addrptmp);
+               *naddrs = 1;
+               return addrp;
+       }
+       if ((addrp = host_to_addr(name, naddrs)) != NULL)
+               return addrp;
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+       static struct in_addr maskaddr;
+       struct in_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 32 bits */
+               maskaddr.s_addr = 0xFFFFFFFF;
+               return &maskaddr;
+       }
+       if ((addrp = dotted_to_addr(mask)) != NULL)
+               /* dotted_to_addr already returns a network byte order addr */
+               return addrp;
+       if (string_to_number(mask, 0, 32, &bits) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", mask);
+       if (bits != 0) {
+               maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+               return &maskaddr;
+       }
+
+       maskaddr.s_addr = 0L;
+       return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+                     struct in_addr *maskp, unsigned int *naddrs)
+{
+       struct in_addr *addrp;
+       char buf[256];
+       char *p;
+       int i, j, k, n;
+
+       strncpy(buf, name, sizeof(buf) - 1);
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               addrp = parse_mask(p + 1);
+       } else
+               addrp = parse_mask(NULL);
+       inaddrcpy(maskp, addrp);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (maskp->s_addr == 0L)
+               strcpy(buf, "0.0.0.0");
+
+       addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+       n = *naddrs;
+       for (i = 0, j = 0; i < n; i++) {
+               addrp[j++].s_addr &= maskp->s_addr;
+               for (k = 0; k < j - 1; k++) {
+                       if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+                               (*naddrs)--;
+                               j--;
+                               break;
+                       }
+               }
+       }
+}
+
+struct iptables_match *
+find_match(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_match *ptr;
+
+       for (ptr = iptables_matches; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified target as match. */
+                       ptr = find_match(name, DONT_LOAD);
+
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load match `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load match `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find match `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+static struct iptables_match *
+find_proto(const char *pname, enum ipt_tryload tryload, int nolookup)
+{
+       unsigned int proto;
+
+       if (string_to_number(pname, 0, 255, &proto) != -1) {
+               char *protoname = proto_to_name(proto, nolookup);
+
+               if (protoname)
+                       return find_match(protoname, tryload);
+       } else
+               return find_match(pname, tryload);
+
+       return NULL;
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+       unsigned int proto;
+
+       if (string_to_number(s, 0, 255, &proto) == -1) {
+               struct protoent *pent;
+
+               if ((pent = getprotobyname(s)))
+                       proto = pent->p_proto;
+               else {
+                       unsigned int i;
+                       for (i = 0;
+                            i < sizeof(chain_protos)/sizeof(struct pprot);
+                            i++) {
+                               if (strcmp(s, chain_protos[i].name) == 0) {
+                                       proto = chain_protos[i].num;
+                                       break;
+                               }
+                       }
+                       if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unknown protocol `%s' specified",
+                                          s);
+               }
+       }
+
+       return (u_int16_t)proto;
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if (vialen == 0)
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (!isalnum(vianame[i]) 
+                           && vianame[i] != '_' 
+                           && vianame[i] != '.') {
+                               printf("Warning: wierd character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+       unsigned int rulenum;
+
+       if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid rule number `%s'", rule);
+
+       return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name `%s' (%i chars max)",
+                          targetname, sizeof(ipt_chainlabel)-1);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+       struct netent *net;
+
+       if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+               return (char *) net->n_name;
+
+       return (char *) NULL;
+}
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+       static char buf[20];
+       const unsigned char *bytep;
+
+       bytep = (const unsigned char *) &(addrp->s_addr);
+       sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+       return buf;
+}
+
+char *
+addr_to_anyname(const struct in_addr *addr)
+{
+       char *name;
+
+       if ((name = addr_to_host(addr)) != NULL ||
+           (name = addr_to_network(addr)) != NULL)
+               return name;
+
+       return addr_to_dotted(addr);
+}
+
+char *
+mask_to_dotted(const struct in_addr *mask)
+{
+       int i;
+       static char buf[20];
+       u_int32_t maskaddr, bits;
+
+       maskaddr = ntohl(mask->s_addr);
+
+       if (maskaddr == 0xFFFFFFFFL)
+               /* we don't want to see "/32" */
+               return "";
+
+       i = 32;
+       bits = 0xFFFFFFFEL;
+       while (--i >= 0 && maskaddr != bits)
+               bits <<= 1;
+       if (i >= 0)
+               sprintf(buf, "/%d", i);
+       else
+               /* mask was not a decent combination of 1's and 0's */
+               sprintf(buf, "/%s", addr_to_dotted(mask));
+
+       return buf;
+}
+
+int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+                unsigned int *ret)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+          int invert)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+
+       if (invert) {
+               unsigned int i;
+               for (i = 0; 1 << i != option; i++);
+
+               if (!inverse_for_options[i])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot have ! before -%c",
+                                  opt2char(option));
+               *invflg |= inverse_for_options[i];
+       }
+}
+
+struct iptables_target *
+find_target(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_target *ptr;
+
+       /* Standard target? */
+       if (strcmp(name, "") == 0
+           || strcmp(name, IPTC_LABEL_ACCEPT) == 0
+           || strcmp(name, IPTC_LABEL_DROP) == 0
+           || strcmp(name, IPTC_LABEL_QUEUE) == 0
+           || strcmp(name, IPTC_LABEL_RETURN) == 0)
+               name = "standard";
+
+       for (ptr = iptables_targets; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified match as a target. */
+                       ptr = find_target(name, DONT_LOAD);
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load target `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load target `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find target `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+             unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+void
+register_match(struct iptables_match *me)
+{
+       struct iptables_match **i;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_match(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: match `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Append to list. */
+       for (i = &iptables_matches; *i; i = &(*i)->next);
+       me->next = NULL;
+       *i = me;
+
+       me->m = NULL;
+       me->mflags = 0;
+}
+
+void
+register_target(struct iptables_target *me)
+{
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_target(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: target `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Prepend to list. */
+       me->next = iptables_targets;
+       iptables_targets = me;
+       me->t = NULL;
+       me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+       if (format & FMT_KILOMEGAGIGA) {
+               if (number > 99999) {
+                       number = (number + 500) / 1000;
+                       if (number > 9999) {
+                               number = (number + 500) / 1000;
+                               if (number > 9999) {
+                                       number = (number + 500) / 1000;
+                                       if (number > 9999) {
+                                               number = (number + 500) / 1000;
+                                               printf(FMT("%4lluT ","%lluT "), number);
+                                       }
+                                       else printf(FMT("%4lluG ","%lluG "), number);
+                               }
+                               else printf(FMT("%4lluM ","%lluM "), number);
+                       } else
+                               printf(FMT("%4lluK ","%lluK "), number);
+               } else
+                       printf(FMT("%5llu ","%llu "), number);
+       } else
+               printf(FMT("%8llu ","%llu "), number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, iptc_handle_t *handle)
+{
+       struct ipt_counters counters;
+       const char *pol = iptc_get_policy(chain, &counters, handle);
+       printf("Chain %s", chain);
+       if (pol) {
+               printf(" (policy %s", pol);
+               if (!(format & FMT_NOCOUNTS)) {
+                       fputc(' ', stdout);
+                       print_num(counters.pcnt, (format|FMT_NOTABLE));
+                       fputs("packets, ", stdout);
+                       print_num(counters.bcnt, (format|FMT_NOTABLE));
+                       fputs("bytes", stdout);
+               }
+               printf(")\n");
+       } else {
+               unsigned int refs;
+               if (!iptc_get_references(&refs, chain, handle))
+                       printf(" (ERROR obtaining refs)\n");
+               else
+                       printf(" (%u references)\n", refs);
+       }
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4s ", "%s "), "num");
+       if (!(format & FMT_NOCOUNTS)) {
+               if (format & FMT_KILOMEGAGIGA) {
+                       printf(FMT("%5s ","%s "), "pkts");
+                       printf(FMT("%5s ","%s "), "bytes");
+               } else {
+                       printf(FMT("%8s ","%s "), "pkts");
+                       printf(FMT("%10s ","%s "), "bytes");
+               }
+       }
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ","%s "), "target");
+       fputs(" prot ", stdout);
+       if (format & FMT_OPTIONS)
+               fputs("opt", stdout);
+       if (format & FMT_VIA) {
+               printf(FMT(" %-6s ","%s "), "in");
+               printf(FMT("%-6s ","%s "), "out");
+       }
+       printf(FMT(" %-19s ","%s "), "source");
+       printf(FMT(" %-19s "," %s "), "destination");
+       printf("\n");
+}
+
+
+static int
+print_match(const struct ipt_entry_match *m,
+           const struct ipt_ip *ip,
+           int numeric)
+{
+       struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD);
+
+       if (match) {
+               if (match->print)
+                       match->print(ip, m, numeric);
+               else
+                       printf("%s ", match->name);
+       } else {
+               if (m->u.user.name[0])
+                       printf("UNKNOWN match `%s' ", m->u.user.name);
+       }
+       /* Don't stop iterating. */
+       return 0;
+}
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct ipt_entry *fw,
+              const char *targname,
+              unsigned int num,
+              unsigned int format,
+              const iptc_handle_t handle)
+{
+       struct iptables_target *target = NULL;
+       const struct ipt_entry_target *t;
+       u_int8_t flags;
+       char buf[BUFSIZ];
+
+       if (!iptc_is_chain(targname, handle))
+               target = find_target(targname, TRY_LOAD);
+       else
+               target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       flags = fw->ip.flags;
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4u ", "%u "), num+1);
+
+       if (!(format & FMT_NOCOUNTS)) {
+               print_num(fw->counters.pcnt, format);
+               print_num(fw->counters.bcnt, format);
+       }
+
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ", "%s "), targname);
+
+       fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
+       {
+               char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
+               if (pname)
+                       printf(FMT("%-5s", "%s "), pname);
+               else
+                       printf(FMT("%-5hu", "%hu "), fw->ip.proto);
+       }
+
+       if (format & FMT_OPTIONS) {
+               if (format & FMT_NOTABLE)
+                       fputs("opt ", stdout);
+               fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
+               fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
+               fputc(' ', stdout);
+       }
+
+       if (format & FMT_VIA) {
+               char iface[IFNAMSIZ+2];
+
+               if (fw->ip.invflags & IPT_INV_VIA_IN) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.iniface[0] != '\0') {
+                       strcat(iface, fw->ip.iniface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT(" %-6s ","in %s "), iface);
+
+               if (fw->ip.invflags & IPT_INV_VIA_OUT) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.outiface[0] != '\0') {
+                       strcat(iface, fw->ip.outiface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT("%-6s ","out %s "), iface);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
+       if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","%s "), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
+               strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
+               printf(FMT("%-19s ","%s "), buf);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
+       if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s","-> %s"), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
+               strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
+               printf(FMT("%-19s","-> %s"), buf);
+       }
+
+       if (format & FMT_NOTABLE)
+               fputs("  ", stdout);
+
+       IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
+
+       if (target) {
+               if (target->print)
+                       /* Print the target information. */
+                       target->print(&fw->ip, t, format & FMT_NUMERIC);
+       } else if (t->u.target_size != sizeof(*t))
+               printf("[%u bytes of unknown target data] ",
+                      t->u.target_size - sizeof(*t));
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ipt_entry *fw,
+                   const iptc_handle_t h)
+{
+       struct ipt_entry_target *t;
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_append_entry(chain, fw, handle);
+               }
+       }
+
+       return ret;
+}
+
+static int
+replace_entry(const ipt_chainlabel chain,
+             struct ipt_entry *fw,
+             unsigned int rulenum,
+             const struct in_addr *saddr,
+             const struct in_addr *daddr,
+             int verbose,
+             iptc_handle_t *handle)
+{
+       fw->ip.src.s_addr = saddr->s_addr;
+       fw->ip.dst.s_addr = daddr->s_addr;
+
+       if (verbose)
+               print_firewall_line(fw, *handle);
+       return iptc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int rulenum,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_insert_entry(chain, fw, rulenum, handle);
+               }
+       }
+
+       return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct ipt_entry *fw)
+{
+       /* Establish mask for comparison */
+       unsigned int size;
+       struct iptables_match *m;
+       unsigned char *mask, *mptr;
+
+       size = sizeof(struct ipt_entry);
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+       }
+
+       mask = fw_calloc(1, size
+                        + IPT_ALIGN(sizeof(struct ipt_entry_target))
+                        + iptables_targets->size);
+
+       memset(mask, 0xFF, sizeof(struct ipt_entry));
+       mptr = mask + sizeof(struct ipt_entry);
+
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memset(mptr, 0xFF,
+                      IPT_ALIGN(sizeof(struct ipt_entry_match))
+                      + m->userspacesize);
+               mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+       }
+
+       memset(mptr, 0xFF,
+              IPT_ALIGN(sizeof(struct ipt_entry_target))
+              + iptables_targets->userspacesize);
+
+       return mask;
+}
+
+static int
+delete_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+       unsigned char *mask;
+
+       mask = make_delete_mask(fw);
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_delete_entry(chain, fw, mask, handle);
+               }
+       }
+       return ret;
+}
+
+int
+for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
+              int verbose, int builtinstoo, iptc_handle_t *handle)
+{
+        int ret = 1;
+       const char *chain;
+       char *chains;
+       unsigned int i, chaincount = 0;
+
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               chaincount++;
+               chain = iptc_next_chain(handle);
+        }
+
+       chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount);
+       i = 0;
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               strcpy(chains + i*sizeof(ipt_chainlabel), chain);
+               i++;
+               chain = iptc_next_chain(handle);
+        }
+
+       for (i = 0; i < chaincount; i++) {
+               if (!builtinstoo
+                   && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
+                                   *handle))
+                       continue;
+               ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
+       }
+
+       free(chains);
+        return ret;
+}
+
+int
+flush_entries(const ipt_chainlabel chain, int verbose,
+             iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(flush_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Flushing chain `%s'\n", chain);
+       return iptc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(zero_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Zeroing chain `%s'\n", chain);
+       return iptc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(delete_chain, verbose, 0, handle);
+
+       if (verbose)
+               fprintf(stdout, "Deleting chain `%s'\n", chain);
+       return iptc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ipt_chainlabel chain, int verbose, int numeric,
+            int expanded, int linenumbers, iptc_handle_t *handle)
+{
+       int found = 0;
+       unsigned int format;
+       const char *this;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       for (this = iptc_first_chain(handle);
+            this;
+            this = iptc_next_chain(handle)) {
+               const struct ipt_entry *i;
+               unsigned int num;
+
+               if (chain && strcmp(chain, this) != 0)
+                       continue;
+
+               if (found) printf("\n");
+
+               print_header(format, this, handle);
+               i = iptc_first_rule(this, handle);
+
+               num = 0;
+               while (i) {
+                       print_firewall(i,
+                                      iptc_get_target(i, handle),
+                                      num++,
+                                      format,
+                                      *handle);
+                       i = iptc_next_rule(i, handle);
+               }
+               found = 1;
+       }
+
+       errno = ENOENT;
+       return found;
+}
+
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = malloc(1024);
+       if (ret) {
+               switch (read(procfile, ret, 1024)) {
+               case -1: goto fail;
+               case 1024: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+int iptables_insmod(const char *modname, const char *modprobe)
+{
+       char *buf = NULL;
+       char *argv[3];
+
+       /* If they don't explicitly set it, read out of kernel */
+       if (!modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+
+       switch (fork()) {
+       case 0:
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               argv[2] = NULL;
+               execv(argv[0], argv);
+
+               /* not usually reached */
+               exit(0);
+       case -1:
+               return -1;
+
+       default: /* parent */
+               wait(NULL);
+       }
+
+       free(buf);
+       return 0;
+}
+
+static struct ipt_entry *
+generate_entry(const struct ipt_entry *fw,
+              struct iptables_match *matches,
+              struct ipt_entry_target *target)
+{
+       unsigned int size;
+       struct iptables_match *m;
+       struct ipt_entry *e;
+
+       size = sizeof(struct ipt_entry);
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += m->m->u.match_size;
+       }
+
+       e = fw_malloc(size + target->u.target_size);
+       *e = *fw;
+       e->target_offset = size;
+       e->next_offset = size + target->u.target_size;
+
+       size = 0;
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memcpy(e->elems + size, m->m, m->m->u.match_size);
+               size += m->m->u.match_size;
+       }
+       memcpy(e->elems + size, target, target->u.target_size);
+
+       return e;
+}
+
+int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
+{
+       struct ipt_entry fw, *e = NULL;
+       int invert = 0;
+       unsigned int nsaddrs = 0, ndaddrs = 0;
+       struct in_addr *saddrs = NULL, *daddrs = NULL;
+
+       int c, verbose = 0;
+       const char *chain = NULL;
+       const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+       const char *policy = NULL, *newname = NULL;
+       unsigned int rulenum = 0, options = 0, command = 0;
+       const char *pcnt = NULL, *bcnt = NULL;
+       int ret = 1;
+       struct iptables_match *m;
+       struct iptables_target *target = NULL;
+       struct iptables_target *t;
+       const char *jumpto = "";
+       char *protocol = NULL;
+       const char *modprobe = NULL;
+       int proto_used = 0;
+
+       memset(&fw, 0, sizeof(fw));
+
+       opts = original_opts;
+       global_option_offset = 0;
+
+       /* re-set optind to 0 in case do_command gets called
+        * a second time */
+       optind = 0;
+
+       /* clear mflags in case do_command gets called a second time
+        * (we clear the global list of all matches for security)*/
+       for (m = iptables_matches; m; m = m->next) {
+               m->mflags = 0;
+               m->used = 0;
+       }
+
+       for (t = iptables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+          "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
+                                          opts, NULL)) != -1) {
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'A':
+                       add_command(&command, CMD_APPEND, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       add_command(&command, CMD_DELETE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!') {
+                               rulenum = parse_rulenumber(argv[optind++]);
+                               command = CMD_DELETE_NUM;
+                       }
+                       break;
+
+               case 'R':
+                       add_command(&command, CMD_REPLACE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a rule number",
+                                          cmd2char(CMD_REPLACE));
+                       break;
+
+               case 'I':
+                       add_command(&command, CMD_INSERT, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else rulenum = 1;
+                       break;
+
+               case 'L':
+                       add_command(&command, CMD_LIST, CMD_ZERO,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'F':
+                       add_command(&command, CMD_FLUSH, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'Z':
+                       add_command(&command, CMD_ZERO, CMD_LIST,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                               && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'N':
+                       if (optarg && *optarg == '-')
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name not allowed to start "
+                                          "with `-'\n");
+                       if (find_target(optarg, TRY_LOAD))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name may not clash "
+                                          "with target name\n");
+                       add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'X':
+                       add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'E':
+                       add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               newname = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires old-chain-name and "
+                                          "new-chain-name",
+                                           cmd2char(CMD_RENAME_CHAIN));
+                       break;
+
+               case 'P':
+                       add_command(&command, CMD_SET_POLICY, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               policy = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a chain and a policy",
+                                          cmd2char(CMD_SET_POLICY));
+                       break;
+
+               case 'h':
+                       if (!optarg)
+                               optarg = argv[optind];
+
+                       /* iptables -p icmp -h */
+                       if (!iptables_matches && protocol)
+                               find_match(protocol, TRY_LOAD);
+
+                       exit_printhelp();
+
+                       /*
+                        * Option selection
+                        */
+               case 'p':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
+                                  invert);
+
+                       /* Canonicalize into lower case */
+                       for (protocol = argv[optind-1]; *protocol; protocol++)
+                               *protocol = tolower(*protocol);
+
+                       protocol = argv[optind-1];
+                       fw.ip.proto = parse_protocol(protocol);
+
+                       if (fw.ip.proto == 0
+                           && (fw.ip.invflags & IPT_INV_PROTO))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "rule would never match protocol");
+                       fw.nfcache |= NFC_IP_PROTO;
+                       break;
+
+               case 's':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_SOURCE, &fw.ip.invflags,
+                                  invert);
+                       shostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP_SRC;
+                       break;
+
+               case 'd':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
+                                  invert);
+                       dhostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP_DST;
+                       break;
+
+               case 'j':
+                       set_option(&options, OPT_JUMP, &fw.ip.invflags,
+                                  invert);
+                       jumpto = parse_target(optarg);
+                       /* TRY_LOAD (may be chain name) */
+                       target = find_target(jumpto, TRY_LOAD);
+
+                       if (target) {
+                               size_t size;
+
+                               size = IPT_ALIGN(sizeof(struct ipt_entry_target))
+                                       + target->size;
+
+                               target->t = fw_calloc(1, size);
+                               target->t->u.target_size = size;
+                               strcpy(target->t->u.user.name, jumpto);
+                               target->init(target->t, &fw.nfcache);
+                               opts = merge_options(opts, target->extra_opts, &target->option_offset);
+                       }
+                       break;
+
+
+               case 'i':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.iniface,
+                                       fw.ip.iniface_mask);
+                       fw.nfcache |= NFC_IP_IF_IN;
+                       break;
+
+               case 'o':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.outiface,
+                                       fw.ip.outiface_mask);
+                       fw.nfcache |= NFC_IP_IF_OUT;
+                       break;
+
+               case 'f':
+                       set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
+                                  invert);
+                       fw.ip.flags |= IPT_F_FRAG;
+                       fw.nfcache |= NFC_IP_FRAG;
+                       break;
+
+               case 'v':
+                       if (!verbose)
+                               set_option(&options, OPT_VERBOSE,
+                                          &fw.ip.invflags, invert);
+                       verbose++;
+                       break;
+
+               case 'm': {
+                       size_t size;
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --match");
+
+                       m = find_match(optarg, LOAD_MUST_SUCCEED);
+                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                        + m->size;
+                       m->m = fw_calloc(1, size);
+                       m->m->u.match_size = size;
+                       strcpy(m->m->u.user.name, m->name);
+                       m->init(m->m, &fw.nfcache);
+                       opts = merge_options(opts, m->extra_opts, &m->option_offset);
+               }
+               break;
+
+               case 'n':
+                       set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 't':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --table");
+                       *table = argv[optind-1];
+                       break;
+
+               case 'x':
+                       set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'V':
+                       if (invert)
+                               printf("Not %s ;-)\n", program_version);
+                       else
+                               printf("%s v%s\n",
+                                      program_name, program_version);
+                       exit(0);
+
+               case '0':
+                       set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'M':
+                       modprobe = optarg;
+                       break;
+
+               case 'c':
+
+                       set_option(&options, OPT_COUNTERS, &fw.ip.invflags,
+                                  invert);
+                       pcnt = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               bcnt = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c requires packet and byte counter",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c packet counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c byte counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+                       
+                       break;
+
+
+               case 1: /* non option */
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "multiple consecutive ! not"
+                                                  " allowed");
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+
+               default:
+                       /* FIXME: This scheme doesn't allow two of the same
+                          matches --RR */
+                       if (!target
+                           || !(target->parse(c - target->option_offset,
+                                              argv, invert,
+                                              &target->tflags,
+                                              &fw, &target->t))) {
+                               for (m = iptables_matches; m; m = m->next) {
+                                       if (!m->used)
+                                               continue;
+
+                                       if (m->parse(c - m->option_offset,
+                                                    argv, invert,
+                                                    &m->mflags,
+                                                    &fw,
+                                                    &fw.nfcache,
+                                                    &m->m))
+                                               break;
+                               }
+
+                               /* If you listen carefully, you can
+                                  actually hear this code suck. */
+
+                               /* some explanations (after four different bugs
+                                * in 3 different releases): If we encountere a
+                                * parameter, that has not been parsed yet,
+                                * it's not an option of an explicitly loaded
+                                * match or a target.  However, we support
+                                * implicit loading of the protocol match
+                                * extension.  '-p tcp' means 'l4 proto 6' and
+                                * at the same time 'load tcp protocol match on
+                                * demand if we specify --dport'.
+                                *
+                                * To make this work, we need to make sure:
+                                * - the parameter has not been parsed by
+                                *   a match (m above)
+                                * - a protocol has been specified
+                                * - the protocol extension has not been
+                                *   loaded yet, or is loaded and unused
+                                *   [think of iptables-restore!]
+                                * - the protocol extension can be successively
+                                *   loaded
+                                */
+                               if (m == NULL
+                                   && protocol
+                                   && (!find_proto(protocol, DONT_LOAD,
+                                                  options&OPT_NUMERIC) 
+                                       || (find_proto(protocol, DONT_LOAD,
+                                                       options&OPT_NUMERIC)
+                                           && (proto_used == 0))
+                                      )
+                                   && (m = find_proto(protocol, TRY_LOAD,
+                                                      options&OPT_NUMERIC))) {
+                                       /* Try loading protocol */
+                                       size_t size;
+                                       
+                                       proto_used = 1;
+
+                                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                                        + m->size;
+
+                                       m->m = fw_calloc(1, size);
+                                       m->m->u.match_size = size;
+                                       strcpy(m->m->u.user.name, m->name);
+                                       m->init(m->m, &fw.nfcache);
+
+                                       opts = merge_options(opts,
+                                           m->extra_opts, &m->option_offset);
+
+                                       optind--;
+                                       continue;
+                               }
+                               if (!m)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind-1]);
+                       }
+               }
+               invert = FALSE;
+       }
+
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               m->final_check(m->mflags);
+       }
+
+       if (target)
+               target->final_check(target->tflags);
+
+       /* Fix me: must put inverse options checking here --MN */
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "nothing appropriate following !");
+
+       if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+               if (!(options & OPT_DESTINATION))
+                       dhostnetworkmask = "0.0.0.0/0";
+               if (!(options & OPT_SOURCE))
+                       shostnetworkmask = "0.0.0.0/0";
+       }
+
+       if (shostnetworkmask)
+               parse_hostnetworkmask(shostnetworkmask, &saddrs,
+                                     &(fw.ip.smsk), &nsaddrs);
+
+       if (dhostnetworkmask)
+               parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+                                     &(fw.ip.dmsk), &ndaddrs);
+
+       if ((nsaddrs > 1 || ndaddrs > 1) &&
+           (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+               exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+                          " source or destination IP addresses");
+
+       if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+               exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+                          "specify a unique address");
+
+       generic_opt_check(command, options);
+
+       if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
+               exit_error(PARAMETER_PROBLEM,
+                          "chain name `%s' too long (must be under %i chars)",
+                          chain, IPT_FUNCTION_MAXNAMELEN);
+
+       /* only allocate handle if we weren't called with a handle */
+       if (!*handle)
+               *handle = iptc_init(*table);
+
+       if (!*handle) {
+               /* try to insmod the module if iptc_init failed */
+               iptables_insmod("ip_tables", modprobe);
+               *handle = iptc_init(*table);
+       }
+
+       if (!*handle)
+               exit_error(VERSION_PROBLEM,
+                          "can't initialize iptables table `%s': %s",
+                          *table, iptc_strerror(errno));
+
+       if (command == CMD_APPEND
+           || command == CMD_DELETE
+           || command == CMD_INSERT
+           || command == CMD_REPLACE) {
+               if (strcmp(chain, "PREROUTING") == 0
+                   || strcmp(chain, "INPUT") == 0) {
+                       /* -o not valid with incoming packets. */
+                       if (options & OPT_VIANAMEOUT)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEOUT),
+                                          chain);
+               }
+
+               if (strcmp(chain, "POSTROUTING") == 0
+                   || strcmp(chain, "OUTPUT") == 0) {
+                       /* -i not valid with outgoing packets */
+                       if (options & OPT_VIANAMEIN)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEIN),
+                                          chain);
+               }
+
+               if (target && iptc_is_chain(jumpto, *handle)) {
+                       printf("Warning: using chain %s, not extension\n",
+                              jumpto);
+
+                       target = NULL;
+               }
+
+               /* If they didn't specify a target, or it's a chain
+                  name, use standard. */
+               if (!target
+                   && (strlen(jumpto) == 0
+                       || iptc_is_chain(jumpto, *handle))) {
+                       size_t size;
+
+                       target = find_target(IPT_STANDARD_TARGET,
+                                            LOAD_MUST_SUCCEED);
+
+                       size = sizeof(struct ipt_entry_target)
+                               + target->size;
+                       target->t = fw_calloc(1, size);
+                       target->t->u.target_size = size;
+                       strcpy(target->t->u.user.name, jumpto);
+                       target->init(target->t, &fw.nfcache);
+               }
+
+               if (!target) {
+                       /* it is no chain, and we can't load a plugin.
+                        * We cannot know if the plugin is corrupt, non
+                        * existant OR if the user just misspelled a
+                        * chain. */
+                       find_target(jumpto, LOAD_MUST_SUCCEED);
+               } else {
+                       e = generate_entry(&fw, iptables_matches, target->t);
+               }
+       }
+
+       switch (command) {
+       case CMD_APPEND:
+               ret = append_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE:
+               ret = delete_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE_NUM:
+               ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
+               break;
+       case CMD_REPLACE:
+               ret = replace_entry(chain, e, rulenum - 1,
+                                   saddrs, daddrs, options&OPT_VERBOSE,
+                                   handle);
+               break;
+       case CMD_INSERT:
+               ret = insert_entry(chain, e, rulenum - 1,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_LIST:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               break;
+       case CMD_FLUSH:
+               ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_ZERO:
+               ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_LIST|CMD_ZERO:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               if (ret)
+                       ret = zero_entries(chain,
+                                          options&OPT_VERBOSE, handle);
+               break;
+       case CMD_NEW_CHAIN:
+               ret = iptc_create_chain(chain, handle);
+               break;
+       case CMD_DELETE_CHAIN:
+               ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_RENAME_CHAIN:
+               ret = iptc_rename_chain(chain, newname, handle);
+               break;
+       case CMD_SET_POLICY:
+               ret = iptc_set_policy(chain, policy, NULL, handle);
+               break;
+       default:
+               /* We should never reach this... */
+               exit_tryhelp(2);
+       }
+
+       if (verbose > 1)
+               dump_entries(*handle);
+
+       return ret;
+}
diff --git a/.#iptables.c.1.55 b/.#iptables.c.1.55
new file mode 100644 (file)
index 0000000..b8087f1
--- /dev/null
@@ -0,0 +1,2303 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC    0x0001
+#define FMT_NOCOUNTS   0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS    0x0008
+#define FMT_NOTABLE    0x0010
+#define FMT_NOTARGET   0x0020
+#define FMT_VIA                0x0040
+#define FMT_NONEWLINE  0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                       | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE               0x0000U
+#define CMD_INSERT             0x0001U
+#define CMD_DELETE             0x0002U
+#define CMD_DELETE_NUM         0x0004U
+#define CMD_REPLACE            0x0008U
+#define CMD_APPEND             0x0010U
+#define CMD_LIST               0x0020U
+#define CMD_FLUSH              0x0040U
+#define CMD_ZERO               0x0080U
+#define CMD_NEW_CHAIN          0x0100U
+#define CMD_DELETE_CHAIN       0x0200U
+#define CMD_SET_POLICY         0x0400U
+#define CMD_CHECK              0x0800U
+#define CMD_RENAME_CHAIN       0x1000U
+#define NUMBER_OF_CMD  13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+                                'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE       0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_SOURCE     0x00002U
+#define OPT_DESTINATION        0x00004U
+#define OPT_PROTOCOL   0x00008U
+#define OPT_JUMP       0x00010U
+#define OPT_VERBOSE    0x00020U
+#define OPT_EXPANDED   0x00040U
+#define OPT_VIANAMEIN  0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_FRAGMENT    0x00200U
+#define OPT_LINENUMBERS 0x00400U
+#define OPT_COUNTERS   0x00800U
+#define NUMBER_OF_OPT  12
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'};
+
+static struct option original_opts[] = {
+       { "append", 1, 0, 'A' },
+       { "delete", 1, 0,  'D' },
+       { "insert", 1, 0,  'I' },
+       { "replace", 1, 0,  'R' },
+       { "list", 2, 0,  'L' },
+       { "flush", 2, 0,  'F' },
+       { "zero", 2, 0,  'Z' },
+       { "new-chain", 1, 0,  'N' },
+       { "delete-chain", 2, 0,  'X' },
+       { "rename-chain", 1, 0,  'E' },
+       { "policy", 1, 0,  'P' },
+       { "source", 1, 0, 's' },
+       { "destination", 1, 0,  'd' },
+       { "src", 1, 0,  's' }, /* synonym */
+       { "dst", 1, 0,  'd' }, /* synonym */
+       { "protocol", 1, 0,  'p' },
+       { "in-interface", 1, 0, 'i' },
+       { "jump", 1, 0, 'j' },
+       { "table", 1, 0, 't' },
+       { "match", 1, 0, 'm' },
+       { "numeric", 0, 0, 'n' },
+       { "out-interface", 1, 0, 'o' },
+       { "verbose", 0, 0, 'v' },
+       { "exact", 0, 0, 'x' },
+       { "fragments", 0, 0, 'f' },
+       { "version", 0, 0, 'V' },
+       { "help", 2, 0, 'h' },
+       { "line-numbers", 0, 0, '0' },
+       { "modprobe", 1, 0, 'M' },
+       { "set-counters", 1, 0, 'c' },
+       { 0 }
+};
+
+/* we need this for iptables-restore.  iptables-restore.c sets line to the
+ * current line of the input file, in order  to give a more precise error
+ * message.  iptables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+#ifndef __OPTIMIZE__
+struct ipt_entry_target *
+ipt_get_target(struct ipt_entry *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+       /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x','x',' '},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*CHECK*/     {'x','+','+','+','x',' ','x',' ',' ',' ','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IPT_INV_SRCIP,
+/* -d */ IPT_INV_DSTIP,
+/* -p */ IPT_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IPT_INV_VIA_IN,
+/* -o */ IPT_INV_VIA_OUT,
+/* -f */ IPT_INV_FRAG,
+/*--line*/ 0
+};
+
+const char *program_version;
+const char *program_name;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct iptables_match *iptables_matches = NULL;
+struct iptables_target *iptables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries(const iptc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmp", IPPROTO_ICMP },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+       unsigned int i;
+
+       if (proto && !nolookup) {
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent)
+                       return pent->p_name;
+       }
+
+       for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+               if (chain_protos[i].num == proto)
+                       return chain_protos[i].name;
+
+       return NULL;
+}
+
+struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+       static struct in_addr addr;
+       unsigned char *addrp;
+       char *p, *q;
+       unsigned int onebyte;
+       int i;
+       char buf[20];
+
+       /* copy dotted string, because we need to modify it */
+       strncpy(buf, dotted, sizeof(buf) - 1);
+       addrp = (unsigned char *) &(addr.s_addr);
+
+       p = buf;
+       for (i = 0; i < 3; i++) {
+               if ((q = strchr(p, '.')) == NULL)
+                       return (struct in_addr *) NULL;
+
+               *q = '\0';
+               if (string_to_number(p, 0, 255, &onebyte) == -1)
+                       return (struct in_addr *) NULL;
+
+               addrp[i] = (unsigned char) onebyte;
+               p = q + 1;
+       }
+
+       /* we've checked 3 bytes, now we check the last one */
+       if (string_to_number(p, 0, 255, &onebyte) == -1)
+               return (struct in_addr *) NULL;
+
+       addrp[3] = (unsigned char) onebyte;
+
+       return &addr;
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+       struct netent *net;
+       static struct in_addr addr;
+
+       if ((net = getnetbyname(name)) != NULL) {
+               if (net->n_addrtype != AF_INET)
+                       return (struct in_addr *) NULL;
+               addr.s_addr = htonl((unsigned long) net->n_net);
+               return &addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+       /* memcpy(dst, src, sizeof(struct in_addr)); */
+       dst->s_addr = src->s_addr;
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       if (status == PARAMETER_PROBLEM)
+               exit_tryhelp(status);
+       if (status == VERSION_PROBLEM)
+               fprintf(stderr,
+                       "Perhaps iptables or your kernel needs to be upgraded.\n");
+       exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+<<<<<<< iptables.c
+       if (line != -1)
+               fprintf(stderr, "Error occurred at line: %d\n", line);
+=======
+       if (line != -1)
+               fprintf(stderr, "Error occured at line: %d\n", line);
+>>>>>>> 1.55
+       fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+                       program_name, program_name );
+       exit(status);
+}
+
+void
+exit_printhelp(void)
+{
+       struct iptables_match *m = NULL;
+       struct iptables_target *t = NULL;
+
+       printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+              program_name, program_version, program_name, program_name,
+              program_name, program_name, program_name, program_name,
+              program_name, program_name);
+
+       printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain          Append to chain\n"
+"  --delete  -D chain          Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"                              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"                              Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"                              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]                List the rules in a chain or all chains\n"
+"  --flush   -F [chain]                Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]                Zero counters in chain or all chains\n"
+"  --new     -N chain          Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]                Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"                              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"                              Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --proto     -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask]\n"
+"                              source specification\n"
+"  --destination -d [!] address[/mask]\n"
+"                              destination specification\n"
+"  --in-interface -i [!] input name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --jump      -j target\n"
+"                              target for rule (may load target extension)\n"
+"  --match     -m match\n"
+"                              extended match (may load extension)\n"
+"  --numeric   -n              numeric output of addresses and ports\n"
+"  --out-interface -o [!] output name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --table     -t table        table to manipulate (default: `filter')\n"
+"  --verbose   -v              verbose mode\n"
+"  --line-numbers              print line numbers when listing\n"
+"  --exact     -x              expand numbers (display exact values)\n"
+"[!] --fragment        -f              match second or further fragments only\n"
+"  --modprobe=<command>                try to insert modules using this command\n"
+"  --set-counters PKTS BYTES   set the counter during insert/append\n"
+"[!] --version -V              print package version.\n");
+
+       /* Print out any special helps. A user might like to be able
+          to add a --help to the commandline, and see expected
+          results. So we call help for all matches & targets */
+       for (t=iptables_targets;t;t=t->next) {
+               printf("\n");
+               t->help();
+       }
+       for (m=iptables_matches;m;m=m->next) {
+               printf("\n");
+               m->help();
+       }
+       exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 0; j < NUMBER_OF_CMD; j++) {
+                       if (!(command & (1<<j)))
+                               continue;
+
+                       if (!(options & (1<<i))) {
+                               if (commands_v_options[j][i] == '+')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "You need to supply the `-%c' "
+                                                  "option for this command\n",
+                                                  optflags[i]);
+                       } else {
+                               if (commands_v_options[j][i] != 'x')
+                                       legal = 1;
+                               else if (legal == 0)
+                                       legal = -1;
+                       }
+               }
+               if (legal == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Illegal option `-%c' with this command\n",
+                                  optflags[i]);
+       }
+}
+
+static char
+opt2char(int option)
+{
+       const char *ptr;
+       for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+       const char *ptr;
+       for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static void
+add_command(int *cmd, const int newcmd, const int othercmds, int invert)
+{
+       if (invert)
+               exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+       if (*cmd & (~othercmds))
+               exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+                          cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+       *cmd |= newcmd;
+}
+
+int
+check_inverse(const char option[], int *invert, int *optind, int argc)
+{
+       if (option && strcmp(option, "!") == 0) {
+               if (*invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Multiple `!' flags not allowed");
+               *invert = TRUE;
+               if (optind) {
+                       *optind = *optind+1;
+                       if (argc && *optind > argc)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "no argument following `!'");
+               }
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+       void *p;
+
+       if ((p = calloc(count, size)) == NULL) {
+               perror("iptables: calloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+       void *p;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("iptables: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+       struct hostent *host;
+       struct in_addr *addr;
+       unsigned int i;
+
+       *naddr = 0;
+       if ((host = gethostbyname(name)) != NULL) {
+               if (host->h_addrtype != AF_INET ||
+                   host->h_length != sizeof(struct in_addr))
+                       return (struct in_addr *) NULL;
+
+               while (host->h_addr_list[*naddr] != (char *) NULL)
+                       (*naddr)++;
+               addr = fw_calloc(*naddr, sizeof(struct in_addr));
+               for (i = 0; i < *naddr; i++)
+                       inaddrcpy(&(addr[i]),
+                                 (struct in_addr *) host->h_addr_list[i]);
+               return addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+       struct hostent *host;
+
+       if ((host = gethostbyaddr((char *) addr,
+                                 sizeof(struct in_addr), AF_INET)) != NULL)
+               return (char *) host->h_name;
+
+       return (char *) NULL;
+}
+
+/*
+ *     All functions starting with "parse" should succeed, otherwise
+ *     the program fails.
+ *     Most routines return pointers to static data that may change
+ *     between calls to the same or other routines with a few exceptions:
+ *     "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *     return global static data.
+*/
+
+static struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+       struct in_addr *addrp, *addrptmp;
+
+       if ((addrptmp = dotted_to_addr(name)) != NULL ||
+           (addrptmp = network_to_addr(name)) != NULL) {
+               addrp = fw_malloc(sizeof(struct in_addr));
+               inaddrcpy(addrp, addrptmp);
+               *naddrs = 1;
+               return addrp;
+       }
+       if ((addrp = host_to_addr(name, naddrs)) != NULL)
+               return addrp;
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+       static struct in_addr maskaddr;
+       struct in_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 32 bits */
+               maskaddr.s_addr = 0xFFFFFFFF;
+               return &maskaddr;
+       }
+       if ((addrp = dotted_to_addr(mask)) != NULL)
+               /* dotted_to_addr already returns a network byte order addr */
+               return addrp;
+       if (string_to_number(mask, 0, 32, &bits) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", mask);
+       if (bits != 0) {
+               maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+               return &maskaddr;
+       }
+
+       maskaddr.s_addr = 0L;
+       return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+                     struct in_addr *maskp, unsigned int *naddrs)
+{
+       struct in_addr *addrp;
+       char buf[256];
+       char *p;
+       int i, j, k, n;
+
+       strncpy(buf, name, sizeof(buf) - 1);
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               addrp = parse_mask(p + 1);
+       } else
+               addrp = parse_mask(NULL);
+       inaddrcpy(maskp, addrp);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (maskp->s_addr == 0L)
+               strcpy(buf, "0.0.0.0");
+
+       addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+       n = *naddrs;
+       for (i = 0, j = 0; i < n; i++) {
+               addrp[j++].s_addr &= maskp->s_addr;
+               for (k = 0; k < j - 1; k++) {
+                       if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+                               (*naddrs)--;
+                               j--;
+                               break;
+                       }
+               }
+       }
+}
+
+struct iptables_match *
+find_match(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_match *ptr;
+
+       for (ptr = iptables_matches; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified target as match. */
+                       ptr = find_match(name, DONT_LOAD);
+
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load match `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load match `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find match `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+static struct iptables_match *
+find_proto(const char *pname, enum ipt_tryload tryload, int nolookup)
+{
+       unsigned int proto;
+
+       if (string_to_number(pname, 0, 255, &proto) != -1) {
+               char *protoname = proto_to_name(proto, nolookup);
+
+               if (protoname)
+                       return find_match(protoname, tryload);
+       } else
+               return find_match(pname, tryload);
+
+       return NULL;
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+       unsigned int proto;
+
+       if (string_to_number(s, 0, 255, &proto) == -1) {
+               struct protoent *pent;
+
+               if ((pent = getprotobyname(s)))
+                       proto = pent->p_proto;
+               else {
+                       unsigned int i;
+                       for (i = 0;
+                            i < sizeof(chain_protos)/sizeof(struct pprot);
+                            i++) {
+                               if (strcmp(s, chain_protos[i].name) == 0) {
+                                       proto = chain_protos[i].num;
+                                       break;
+                               }
+                       }
+                       if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unknown protocol `%s' specified",
+                                          s);
+               }
+       }
+
+       return (u_int16_t)proto;
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if (vialen == 0)
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (!isalnum(vianame[i]) 
+                           && vianame[i] != '_' 
+                           && vianame[i] != '.') {
+                               printf("Warning: wierd character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+       unsigned int rulenum;
+
+       if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid rule number `%s'", rule);
+
+       return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name `%s' (%i chars max)",
+                          targetname, sizeof(ipt_chainlabel)-1);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+       struct netent *net;
+
+       if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+               return (char *) net->n_name;
+
+       return (char *) NULL;
+}
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+       static char buf[20];
+       const unsigned char *bytep;
+
+       bytep = (const unsigned char *) &(addrp->s_addr);
+       sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+       return buf;
+}
+
+char *
+addr_to_anyname(const struct in_addr *addr)
+{
+       char *name;
+
+       if ((name = addr_to_host(addr)) != NULL ||
+           (name = addr_to_network(addr)) != NULL)
+               return name;
+
+       return addr_to_dotted(addr);
+}
+
+char *
+mask_to_dotted(const struct in_addr *mask)
+{
+       int i;
+       static char buf[20];
+       u_int32_t maskaddr, bits;
+
+       maskaddr = ntohl(mask->s_addr);
+
+       if (maskaddr == 0xFFFFFFFFL)
+               /* we don't want to see "/32" */
+               return "";
+
+       i = 32;
+       bits = 0xFFFFFFFEL;
+       while (--i >= 0 && maskaddr != bits)
+               bits <<= 1;
+       if (i >= 0)
+               sprintf(buf, "/%d", i);
+       else
+               /* mask was not a decent combination of 1's and 0's */
+               sprintf(buf, "/%s", addr_to_dotted(mask));
+
+       return buf;
+}
+
+int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+                unsigned int *ret)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+          int invert)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+
+       if (invert) {
+               unsigned int i;
+               for (i = 0; 1 << i != option; i++);
+
+               if (!inverse_for_options[i])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot have ! before -%c",
+                                  opt2char(option));
+               *invflg |= inverse_for_options[i];
+       }
+}
+
+struct iptables_target *
+find_target(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_target *ptr;
+
+       /* Standard target? */
+       if (strcmp(name, "") == 0
+           || strcmp(name, IPTC_LABEL_ACCEPT) == 0
+           || strcmp(name, IPTC_LABEL_DROP) == 0
+           || strcmp(name, IPTC_LABEL_QUEUE) == 0
+           || strcmp(name, IPTC_LABEL_RETURN) == 0)
+               name = "standard";
+
+       for (ptr = iptables_targets; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified match as a target. */
+                       ptr = find_target(name, DONT_LOAD);
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load target `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load target `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find target `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+             unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+void
+register_match(struct iptables_match *me)
+{
+       struct iptables_match **i;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_match(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: match `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Append to list. */
+       for (i = &iptables_matches; *i; i = &(*i)->next);
+       me->next = NULL;
+       *i = me;
+
+       me->m = NULL;
+       me->mflags = 0;
+}
+
+void
+register_target(struct iptables_target *me)
+{
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_target(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: target `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Prepend to list. */
+       me->next = iptables_targets;
+       iptables_targets = me;
+       me->t = NULL;
+       me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+       if (format & FMT_KILOMEGAGIGA) {
+               if (number > 99999) {
+                       number = (number + 500) / 1000;
+                       if (number > 9999) {
+                               number = (number + 500) / 1000;
+                               if (number > 9999) {
+                                       number = (number + 500) / 1000;
+                                       if (number > 9999) {
+                                               number = (number + 500) / 1000;
+                                               printf(FMT("%4lluT ","%lluT "), number);
+                                       }
+                                       else printf(FMT("%4lluG ","%lluG "), number);
+                               }
+                               else printf(FMT("%4lluM ","%lluM "), number);
+                       } else
+                               printf(FMT("%4lluK ","%lluK "), number);
+               } else
+                       printf(FMT("%5llu ","%llu "), number);
+       } else
+               printf(FMT("%8llu ","%llu "), number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, iptc_handle_t *handle)
+{
+       struct ipt_counters counters;
+       const char *pol = iptc_get_policy(chain, &counters, handle);
+       printf("Chain %s", chain);
+       if (pol) {
+               printf(" (policy %s", pol);
+               if (!(format & FMT_NOCOUNTS)) {
+                       fputc(' ', stdout);
+                       print_num(counters.pcnt, (format|FMT_NOTABLE));
+                       fputs("packets, ", stdout);
+                       print_num(counters.bcnt, (format|FMT_NOTABLE));
+                       fputs("bytes", stdout);
+               }
+               printf(")\n");
+       } else {
+               unsigned int refs;
+               if (!iptc_get_references(&refs, chain, handle))
+                       printf(" (ERROR obtaining refs)\n");
+               else
+                       printf(" (%u references)\n", refs);
+       }
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4s ", "%s "), "num");
+       if (!(format & FMT_NOCOUNTS)) {
+               if (format & FMT_KILOMEGAGIGA) {
+                       printf(FMT("%5s ","%s "), "pkts");
+                       printf(FMT("%5s ","%s "), "bytes");
+               } else {
+                       printf(FMT("%8s ","%s "), "pkts");
+                       printf(FMT("%10s ","%s "), "bytes");
+               }
+       }
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ","%s "), "target");
+       fputs(" prot ", stdout);
+       if (format & FMT_OPTIONS)
+               fputs("opt", stdout);
+       if (format & FMT_VIA) {
+               printf(FMT(" %-6s ","%s "), "in");
+               printf(FMT("%-6s ","%s "), "out");
+       }
+       printf(FMT(" %-19s ","%s "), "source");
+       printf(FMT(" %-19s "," %s "), "destination");
+       printf("\n");
+}
+
+
+static int
+print_match(const struct ipt_entry_match *m,
+           const struct ipt_ip *ip,
+           int numeric)
+{
+       struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD);
+
+       if (match) {
+               if (match->print)
+                       match->print(ip, m, numeric);
+               else
+                       printf("%s ", match->name);
+       } else {
+               if (m->u.user.name[0])
+                       printf("UNKNOWN match `%s' ", m->u.user.name);
+       }
+       /* Don't stop iterating. */
+       return 0;
+}
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct ipt_entry *fw,
+              const char *targname,
+              unsigned int num,
+              unsigned int format,
+              const iptc_handle_t handle)
+{
+       struct iptables_target *target = NULL;
+       const struct ipt_entry_target *t;
+       u_int8_t flags;
+       char buf[BUFSIZ];
+
+       if (!iptc_is_chain(targname, handle))
+               target = find_target(targname, TRY_LOAD);
+       else
+               target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       flags = fw->ip.flags;
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4u ", "%u "), num+1);
+
+       if (!(format & FMT_NOCOUNTS)) {
+               print_num(fw->counters.pcnt, format);
+               print_num(fw->counters.bcnt, format);
+       }
+
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ", "%s "), targname);
+
+       fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
+       {
+               char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
+               if (pname)
+                       printf(FMT("%-5s", "%s "), pname);
+               else
+                       printf(FMT("%-5hu", "%hu "), fw->ip.proto);
+       }
+
+       if (format & FMT_OPTIONS) {
+               if (format & FMT_NOTABLE)
+                       fputs("opt ", stdout);
+               fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
+               fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
+               fputc(' ', stdout);
+       }
+
+       if (format & FMT_VIA) {
+               char iface[IFNAMSIZ+2];
+
+               if (fw->ip.invflags & IPT_INV_VIA_IN) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.iniface[0] != '\0') {
+                       strcat(iface, fw->ip.iniface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT(" %-6s ","in %s "), iface);
+
+               if (fw->ip.invflags & IPT_INV_VIA_OUT) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.outiface[0] != '\0') {
+                       strcat(iface, fw->ip.outiface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT("%-6s ","out %s "), iface);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
+       if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","%s "), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
+               strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
+               printf(FMT("%-19s ","%s "), buf);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
+       if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s","-> %s"), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
+               strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
+               printf(FMT("%-19s","-> %s"), buf);
+       }
+
+       if (format & FMT_NOTABLE)
+               fputs("  ", stdout);
+
+       IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
+
+       if (target) {
+               if (target->print)
+                       /* Print the target information. */
+                       target->print(&fw->ip, t, format & FMT_NUMERIC);
+       } else if (t->u.target_size != sizeof(*t))
+               printf("[%u bytes of unknown target data] ",
+                      t->u.target_size - sizeof(*t));
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ipt_entry *fw,
+                   const iptc_handle_t h)
+{
+       struct ipt_entry_target *t;
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_append_entry(chain, fw, handle);
+               }
+       }
+
+       return ret;
+}
+
+static int
+replace_entry(const ipt_chainlabel chain,
+             struct ipt_entry *fw,
+             unsigned int rulenum,
+             const struct in_addr *saddr,
+             const struct in_addr *daddr,
+             int verbose,
+             iptc_handle_t *handle)
+{
+       fw->ip.src.s_addr = saddr->s_addr;
+       fw->ip.dst.s_addr = daddr->s_addr;
+
+       if (verbose)
+               print_firewall_line(fw, *handle);
+       return iptc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int rulenum,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_insert_entry(chain, fw, rulenum, handle);
+               }
+       }
+
+       return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct ipt_entry *fw)
+{
+       /* Establish mask for comparison */
+       unsigned int size;
+       struct iptables_match *m;
+       unsigned char *mask, *mptr;
+
+       size = sizeof(struct ipt_entry);
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+       }
+
+       mask = fw_calloc(1, size
+                        + IPT_ALIGN(sizeof(struct ipt_entry_target))
+                        + iptables_targets->size);
+
+       memset(mask, 0xFF, sizeof(struct ipt_entry));
+       mptr = mask + sizeof(struct ipt_entry);
+
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memset(mptr, 0xFF,
+                      IPT_ALIGN(sizeof(struct ipt_entry_match))
+                      + m->userspacesize);
+               mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+       }
+
+       memset(mptr, 0xFF,
+              IPT_ALIGN(sizeof(struct ipt_entry_target))
+              + iptables_targets->userspacesize);
+
+       return mask;
+}
+
+static int
+delete_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+       unsigned char *mask;
+
+       mask = make_delete_mask(fw);
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_delete_entry(chain, fw, mask, handle);
+               }
+       }
+       return ret;
+}
+
+int
+for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
+              int verbose, int builtinstoo, iptc_handle_t *handle)
+{
+        int ret = 1;
+       const char *chain;
+       char *chains;
+       unsigned int i, chaincount = 0;
+
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               chaincount++;
+               chain = iptc_next_chain(handle);
+        }
+
+       chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount);
+       i = 0;
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               strcpy(chains + i*sizeof(ipt_chainlabel), chain);
+               i++;
+               chain = iptc_next_chain(handle);
+        }
+
+       for (i = 0; i < chaincount; i++) {
+               if (!builtinstoo
+                   && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
+                                   *handle))
+                       continue;
+               ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
+       }
+
+       free(chains);
+        return ret;
+}
+
+int
+flush_entries(const ipt_chainlabel chain, int verbose,
+             iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(flush_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Flushing chain `%s'\n", chain);
+       return iptc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(zero_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Zeroing chain `%s'\n", chain);
+       return iptc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(delete_chain, verbose, 0, handle);
+
+       if (verbose)
+               fprintf(stdout, "Deleting chain `%s'\n", chain);
+       return iptc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ipt_chainlabel chain, int verbose, int numeric,
+            int expanded, int linenumbers, iptc_handle_t *handle)
+{
+       int found = 0;
+       unsigned int format;
+       const char *this;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       for (this = iptc_first_chain(handle);
+            this;
+            this = iptc_next_chain(handle)) {
+               const struct ipt_entry *i;
+               unsigned int num;
+
+               if (chain && strcmp(chain, this) != 0)
+                       continue;
+
+               if (found) printf("\n");
+
+               print_header(format, this, handle);
+               i = iptc_first_rule(this, handle);
+
+               num = 0;
+               while (i) {
+                       print_firewall(i,
+                                      iptc_get_target(i, handle),
+                                      num++,
+                                      format,
+                                      *handle);
+                       i = iptc_next_rule(i, handle);
+               }
+               found = 1;
+       }
+
+       errno = ENOENT;
+       return found;
+}
+
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = malloc(1024);
+       if (ret) {
+               switch (read(procfile, ret, 1024)) {
+               case -1: goto fail;
+               case 1024: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+int iptables_insmod(const char *modname, const char *modprobe)
+{
+       char *buf = NULL;
+       char *argv[3];
+
+       /* If they don't explicitly set it, read out of kernel */
+       if (!modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+
+       switch (fork()) {
+       case 0:
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               argv[2] = NULL;
+               execv(argv[0], argv);
+
+               /* not usually reached */
+               exit(0);
+       case -1:
+               return -1;
+
+       default: /* parent */
+               wait(NULL);
+       }
+
+       free(buf);
+       return 0;
+}
+
+static struct ipt_entry *
+generate_entry(const struct ipt_entry *fw,
+              struct iptables_match *matches,
+              struct ipt_entry_target *target)
+{
+       unsigned int size;
+       struct iptables_match *m;
+       struct ipt_entry *e;
+
+       size = sizeof(struct ipt_entry);
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += m->m->u.match_size;
+       }
+
+       e = fw_malloc(size + target->u.target_size);
+       *e = *fw;
+       e->target_offset = size;
+       e->next_offset = size + target->u.target_size;
+
+       size = 0;
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memcpy(e->elems + size, m->m, m->m->u.match_size);
+               size += m->m->u.match_size;
+       }
+       memcpy(e->elems + size, target, target->u.target_size);
+
+       return e;
+}
+
+int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
+{
+       struct ipt_entry fw, *e = NULL;
+       int invert = 0;
+       unsigned int nsaddrs = 0, ndaddrs = 0;
+       struct in_addr *saddrs = NULL, *daddrs = NULL;
+
+       int c, verbose = 0;
+       const char *chain = NULL;
+       const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+       const char *policy = NULL, *newname = NULL;
+       unsigned int rulenum = 0, options = 0, command = 0;
+       const char *pcnt = NULL, *bcnt = NULL;
+       int ret = 1;
+       struct iptables_match *m;
+       struct iptables_target *target = NULL;
+       struct iptables_target *t;
+       const char *jumpto = "";
+       char *protocol = NULL;
+       const char *modprobe = NULL;
+       int proto_used = 0;
+
+       memset(&fw, 0, sizeof(fw));
+
+       opts = original_opts;
+       global_option_offset = 0;
+
+       /* re-set optind to 0 in case do_command gets called
+        * a second time */
+       optind = 0;
+
+       /* clear mflags in case do_command gets called a second time
+        * (we clear the global list of all matches for security)*/
+       for (m = iptables_matches; m; m = m->next) {
+               m->mflags = 0;
+               m->used = 0;
+       }
+
+       for (t = iptables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+          "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
+                                          opts, NULL)) != -1) {
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'A':
+                       add_command(&command, CMD_APPEND, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       add_command(&command, CMD_DELETE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!') {
+                               rulenum = parse_rulenumber(argv[optind++]);
+                               command = CMD_DELETE_NUM;
+                       }
+                       break;
+
+               case 'R':
+                       add_command(&command, CMD_REPLACE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a rule number",
+                                          cmd2char(CMD_REPLACE));
+                       break;
+
+               case 'I':
+                       add_command(&command, CMD_INSERT, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else rulenum = 1;
+                       break;
+
+               case 'L':
+                       add_command(&command, CMD_LIST, CMD_ZERO,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'F':
+                       add_command(&command, CMD_FLUSH, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'Z':
+                       add_command(&command, CMD_ZERO, CMD_LIST,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                               && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'N':
+                       if (optarg && *optarg == '-')
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name not allowed to start "
+                                          "with `-'\n");
+                       if (find_target(optarg, TRY_LOAD))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name may not clash "
+                                          "with target name\n");
+                       add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'X':
+                       add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'E':
+                       add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               newname = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires old-chain-name and "
+                                          "new-chain-name",
+                                           cmd2char(CMD_RENAME_CHAIN));
+                       break;
+
+               case 'P':
+                       add_command(&command, CMD_SET_POLICY, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               policy = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a chain and a policy",
+                                          cmd2char(CMD_SET_POLICY));
+                       break;
+
+               case 'h':
+                       if (!optarg)
+                               optarg = argv[optind];
+
+                       /* iptables -p icmp -h */
+                       if (!iptables_matches && protocol)
+                               find_match(protocol, TRY_LOAD);
+
+                       exit_printhelp();
+
+                       /*
+                        * Option selection
+                        */
+               case 'p':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
+                                  invert);
+
+                       /* Canonicalize into lower case */
+                       for (protocol = argv[optind-1]; *protocol; protocol++)
+                               *protocol = tolower(*protocol);
+
+                       protocol = argv[optind-1];
+                       fw.ip.proto = parse_protocol(protocol);
+
+                       if (fw.ip.proto == 0
+                           && (fw.ip.invflags & IPT_INV_PROTO))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "rule would never match protocol");
+                       fw.nfcache |= NFC_IP_PROTO;
+                       break;
+
+               case 's':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_SOURCE, &fw.ip.invflags,
+                                  invert);
+                       shostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP_SRC;
+                       break;
+
+               case 'd':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
+                                  invert);
+                       dhostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP_DST;
+                       break;
+
+               case 'j':
+                       set_option(&options, OPT_JUMP, &fw.ip.invflags,
+                                  invert);
+                       jumpto = parse_target(optarg);
+                       /* TRY_LOAD (may be chain name) */
+                       target = find_target(jumpto, TRY_LOAD);
+
+                       if (target) {
+                               size_t size;
+
+                               size = IPT_ALIGN(sizeof(struct ipt_entry_target))
+                                       + target->size;
+
+                               target->t = fw_calloc(1, size);
+                               target->t->u.target_size = size;
+                               strcpy(target->t->u.user.name, jumpto);
+                               target->init(target->t, &fw.nfcache);
+                               opts = merge_options(opts, target->extra_opts, &target->option_offset);
+                       }
+                       break;
+
+
+               case 'i':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.iniface,
+                                       fw.ip.iniface_mask);
+                       fw.nfcache |= NFC_IP_IF_IN;
+                       break;
+
+               case 'o':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.outiface,
+                                       fw.ip.outiface_mask);
+                       fw.nfcache |= NFC_IP_IF_OUT;
+                       break;
+
+               case 'f':
+                       set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
+                                  invert);
+                       fw.ip.flags |= IPT_F_FRAG;
+                       fw.nfcache |= NFC_IP_FRAG;
+                       break;
+
+               case 'v':
+                       if (!verbose)
+                               set_option(&options, OPT_VERBOSE,
+                                          &fw.ip.invflags, invert);
+                       verbose++;
+                       break;
+
+               case 'm': {
+                       size_t size;
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --match");
+
+                       m = find_match(optarg, LOAD_MUST_SUCCEED);
+                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                        + m->size;
+                       m->m = fw_calloc(1, size);
+                       m->m->u.match_size = size;
+                       strcpy(m->m->u.user.name, m->name);
+                       m->init(m->m, &fw.nfcache);
+                       opts = merge_options(opts, m->extra_opts, &m->option_offset);
+               }
+               break;
+
+               case 'n':
+                       set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 't':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --table");
+                       *table = argv[optind-1];
+                       break;
+
+               case 'x':
+                       set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'V':
+                       if (invert)
+                               printf("Not %s ;-)\n", program_version);
+                       else
+                               printf("%s v%s\n",
+                                      program_name, program_version);
+                       exit(0);
+
+               case '0':
+                       set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'M':
+                       modprobe = optarg;
+                       break;
+
+               case 'c':
+
+                       set_option(&options, OPT_COUNTERS, &fw.ip.invflags,
+                                  invert);
+                       pcnt = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               bcnt = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c requires packet and byte counter",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c packet counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c byte counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+                       
+                       break;
+
+
+               case 1: /* non option */
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "multiple consecutive ! not"
+                                                  " allowed");
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+
+               default:
+                       /* FIXME: This scheme doesn't allow two of the same
+                          matches --RR */
+                       if (!target
+                           || !(target->parse(c - target->option_offset,
+                                              argv, invert,
+                                              &target->tflags,
+                                              &fw, &target->t))) {
+                               for (m = iptables_matches; m; m = m->next) {
+                                       if (!m->used)
+                                               continue;
+
+                                       if (m->parse(c - m->option_offset,
+                                                    argv, invert,
+                                                    &m->mflags,
+                                                    &fw,
+                                                    &fw.nfcache,
+                                                    &m->m))
+                                               break;
+                               }
+
+                               /* If you listen carefully, you can
+                                  actually hear this code suck. */
+
+                               /* some explanations (after four different bugs
+                                * in 3 different releases): If we encountere a
+                                * parameter, that has not been parsed yet,
+                                * it's not an option of an explicitly loaded
+                                * match or a target.  However, we support
+                                * implicit loading of the protocol match
+                                * extension.  '-p tcp' means 'l4 proto 6' and
+                                * at the same time 'load tcp protocol match on
+                                * demand if we specify --dport'.
+                                *
+                                * To make this work, we need to make sure:
+                                * - the parameter has not been parsed by
+                                *   a match (m above)
+                                * - a protocol has been specified
+                                * - the protocol extension has not been
+                                *   loaded yet, or is loaded and unused
+                                *   [think of iptables-restore!]
+                                * - the protocol extension can be successively
+                                *   loaded
+                                */
+                               if (m == NULL
+                                   && protocol
+                                   && (!find_proto(protocol, DONT_LOAD,
+                                                  options&OPT_NUMERIC) 
+                                       || (find_proto(protocol, DONT_LOAD,
+                                                       options&OPT_NUMERIC)
+                                           && (proto_used == 0))
+                                      )
+                                   && (m = find_proto(protocol, TRY_LOAD,
+                                                      options&OPT_NUMERIC))) {
+                                       /* Try loading protocol */
+                                       size_t size;
+                                       
+                                       proto_used = 1;
+
+                                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                                        + m->size;
+
+                                       m->m = fw_calloc(1, size);
+                                       m->m->u.match_size = size;
+                                       strcpy(m->m->u.user.name, m->name);
+                                       m->init(m->m, &fw.nfcache);
+
+                                       opts = merge_options(opts,
+                                           m->extra_opts, &m->option_offset);
+
+                                       optind--;
+                                       continue;
+                               }
+                               if (!m)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind-1]);
+                       }
+               }
+               invert = FALSE;
+       }
+
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               m->final_check(m->mflags);
+       }
+
+       if (target)
+               target->final_check(target->tflags);
+
+       /* Fix me: must put inverse options checking here --MN */
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "nothing appropriate following !");
+
+       if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+               if (!(options & OPT_DESTINATION))
+                       dhostnetworkmask = "0.0.0.0/0";
+               if (!(options & OPT_SOURCE))
+                       shostnetworkmask = "0.0.0.0/0";
+       }
+
+       if (shostnetworkmask)
+               parse_hostnetworkmask(shostnetworkmask, &saddrs,
+                                     &(fw.ip.smsk), &nsaddrs);
+
+       if (dhostnetworkmask)
+               parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+                                     &(fw.ip.dmsk), &ndaddrs);
+
+       if ((nsaddrs > 1 || ndaddrs > 1) &&
+           (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+               exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+                          " source or destination IP addresses");
+
+       if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+               exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+                          "specify a unique address");
+
+       generic_opt_check(command, options);
+
+       if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
+               exit_error(PARAMETER_PROBLEM,
+                          "chain name `%s' too long (must be under %i chars)",
+                          chain, IPT_FUNCTION_MAXNAMELEN);
+
+       /* only allocate handle if we weren't called with a handle */
+       if (!*handle)
+               *handle = iptc_init(*table);
+
+       if (!*handle) {
+               /* try to insmod the module if iptc_init failed */
+               iptables_insmod("ip_tables", modprobe);
+               *handle = iptc_init(*table);
+       }
+
+       if (!*handle)
+               exit_error(VERSION_PROBLEM,
+                          "can't initialize iptables table `%s': %s",
+                          *table, iptc_strerror(errno));
+
+       if (command == CMD_APPEND
+           || command == CMD_DELETE
+           || command == CMD_INSERT
+           || command == CMD_REPLACE) {
+               if (strcmp(chain, "PREROUTING") == 0
+                   || strcmp(chain, "INPUT") == 0) {
+                       /* -o not valid with incoming packets. */
+                       if (options & OPT_VIANAMEOUT)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEOUT),
+                                          chain);
+               }
+
+               if (strcmp(chain, "POSTROUTING") == 0
+                   || strcmp(chain, "OUTPUT") == 0) {
+                       /* -i not valid with outgoing packets */
+                       if (options & OPT_VIANAMEIN)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEIN),
+                                          chain);
+               }
+
+               if (target && iptc_is_chain(jumpto, *handle)) {
+                       printf("Warning: using chain %s, not extension\n",
+                              jumpto);
+
+                       target = NULL;
+               }
+
+               /* If they didn't specify a target, or it's a chain
+                  name, use standard. */
+               if (!target
+                   && (strlen(jumpto) == 0
+                       || iptc_is_chain(jumpto, *handle))) {
+                       size_t size;
+
+                       target = find_target(IPT_STANDARD_TARGET,
+                                            LOAD_MUST_SUCCEED);
+
+                       size = sizeof(struct ipt_entry_target)
+                               + target->size;
+                       target->t = fw_calloc(1, size);
+                       target->t->u.target_size = size;
+                       strcpy(target->t->u.user.name, jumpto);
+                       target->init(target->t, &fw.nfcache);
+               }
+
+               if (!target) {
+                       /* it is no chain, and we can't load a plugin.
+                        * We cannot know if the plugin is corrupt, non
+                        * existant OR if the user just misspelled a
+                        * chain. */
+                       find_target(jumpto, LOAD_MUST_SUCCEED);
+               } else {
+                       e = generate_entry(&fw, iptables_matches, target->t);
+               }
+       }
+
+       switch (command) {
+       case CMD_APPEND:
+               ret = append_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE:
+               ret = delete_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE_NUM:
+               ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
+               break;
+       case CMD_REPLACE:
+               ret = replace_entry(chain, e, rulenum - 1,
+                                   saddrs, daddrs, options&OPT_VERBOSE,
+                                   handle);
+               break;
+       case CMD_INSERT:
+               ret = insert_entry(chain, e, rulenum - 1,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_LIST:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               break;
+       case CMD_FLUSH:
+               ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_ZERO:
+               ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_LIST|CMD_ZERO:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               if (ret)
+                       ret = zero_entries(chain,
+                                          options&OPT_VERBOSE, handle);
+               break;
+       case CMD_NEW_CHAIN:
+               ret = iptc_create_chain(chain, handle);
+               break;
+       case CMD_DELETE_CHAIN:
+               ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_RENAME_CHAIN:
+               ret = iptc_rename_chain(chain, newname, handle);
+               break;
+       case CMD_SET_POLICY:
+               ret = iptc_set_policy(chain, policy, NULL, handle);
+               break;
+       default:
+               /* We should never reach this... */
+               exit_tryhelp(2);
+       }
+
+       if (verbose > 1)
+               dump_entries(*handle);
+
+       return ret;
+}
diff --git a/Makefile.nolibnsl b/Makefile.nolibnsl
new file mode 100644 (file)
index 0000000..622a680
--- /dev/null
@@ -0,0 +1,208 @@
+# uncomment this to get a fully statically linked version
+# NO_SHARED_LIBS = 1
+
+# uncomment this to disable IPv6 support
+# DO_IPV6 = 0
+
+######################################################################
+# YOU SHOULD NOT NEED TO TOUCH ANYTHING BELOW THIS LINE
+######################################################################
+
+# Standard part of Makefile for topdir.
+TOPLEVEL_INCLUDED=YES
+
+ifndef KERNEL_DIR
+KERNEL_DIR=/usr/src/linux
+endif
+IPTABLES_VERSION:=1.2.9
+OLD_IPTABLES_VERSION:=1.2.8
+
+PREFIX:=/usr
+LIBDIR:=$(PREFIX)/lib
+BINDIR:=$(PREFIX)/sbin
+MANDIR:=$(PREFIX)/man
+INCDIR:=$(PREFIX)/include
+
+# directory for new iptables releases
+RELEASE_DIR:=/tmp
+
+# Need libc6 for this.  FIXME: Should covert to autoconf.
+ifeq ($(shell [ -f /usr/include/netinet/ip6.h ] && echo YES), YES)
+DO_IPV6:=1
+endif
+
+COPT_FLAGS:=-O2
+CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -I$(KERNEL_DIR)/include -Iinclude/ -DIPTABLES_VERSION=\"$(IPTABLES_VERSION)\" #-g -DDEBUG #-pg # -DIPTC_DEBUG
+
+ifdef NO_SHARED_LIBS
+CFLAGS += -DNO_SHARED_LIBS=1
+endif
+
+ifndef NO_SHARED_LIBS
+DEPFILES = $(SHARED_LIBS:%.so=%.d)
+SH_CFLAGS:=$(CFLAGS) -fPIC
+STATIC_LIBS  =
+STATIC6_LIBS =
+LDFLAGS      = -rdynamic
+LDLIBS       = -ldl -lnsl
+else
+DEPFILES = $(EXT_OBJS:%.o=%.d)
+STATIC_LIBS  = extensions/libext.a
+STATIC6_LIBS = extensions/libext6.a
+LDFLAGS      = -static
+LDLIBS       =
+endif
+
+EXTRAS+=iptables iptables.o
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables $(DESTDIR)$(MANDIR)/man8/iptables.8
+
+# No longer experimental.
+EXTRAS+=iptables-save iptables-restore
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables-save $(DESTDIR)$(BINDIR)/iptables-restore $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8
+
+ifeq ($(DO_IPV6), 1)
+EXTRAS+=ip6tables ip6tables.o
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ip6tables $(DESTDIR)$(MANDIR)/man8/ip6tables.8
+EXTRAS_EXP+=ip6tables-save ip6tables-restore
+EXTRA_INSTALLS_EXP+=$(DESTDIR)$(BINDIR)/ip6tables-save $(DESTDIR)$(BINDIR)/ip6tables-restore # $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-restore.8
+endif
+
+# Sparc64 hack
+ifeq ($(shell uname -m),sparc64)
+# The kernel is 64-bit, even though userspace is 32.
+CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+endif
+
+# HPPA hack
+ifeq ($(shell uname -m),parisc64)
+# The kernel is 64-bit, even though userspace is 32.
+CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+endif
+
+ifndef IPT_LIBDIR
+IPT_LIBDIR:=$(LIBDIR)/iptables
+endif
+
+.PHONY: default
+default: print-extensions all
+
+.PHONY: print-extensions
+print-extensions:
+       @[ -n "$(OPTIONALS)" ] && echo Extensions found: $(OPTIONALS)
+
+iptables.o: iptables.c
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
+
+iptables: iptables-standalone.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/iptables: iptables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+iptables-save: iptables-save.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/iptables-save: iptables-save
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+iptables-restore: iptables-restore.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/iptables-restore: iptables-restore
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+ip6tables.o: ip6tables.c
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
+
+ip6tables: ip6tables-standalone.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables: ip6tables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+ip6tables-save: ip6tables-save.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables-save: ip6tables-save
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+ip6tables-restore: ip6tables-restore.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables-restore: ip6tables-restore
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+$(DESTDIR)$(MANDIR)/man8/%.8: %.8
+       @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
+       cp $< $@
+
+EXTRA_DEPENDS+=iptables-standalone.d iptables.d
+
+iptables-standalone.d iptables.d: %.d: %.c
+       @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.o:@' > $@
+
+
+# Development Targets
+.PHONY: install-devel-man3
+install-devel-man3: $(DEVEL_MAN3)
+       @[ -d $(DESTDIR)$(MANDIR)/man3 ] || mkdir -p $(DESTDIR)$(MANDIR)/man3
+       @cp -v $(DEVEL_MAN3) $(DESTDIR)$(MANDIR)/man3
+
+.PHONY: install-devel-headers
+install-devel-headers: $(DEVEL_HEADERS)
+       @[ -d $(DESTDIR)$(INCDIR) ] || mkdir -p $(DESTDIR)$(INCDIR)
+       @cp -v $(DEVEL_HEADERS) $(DESTDIR)$(INCDIR)
+
+.PHONY: install-devel-libs
+install-devel-libs: $(DEVEL_LIBS)
+       @[ -d $(DESTDIR)$(LIBDIR) ] || mkdir -p $(DESTDIR)$(LIBDIR)
+       @cp -v $(DEVEL_LIBS) $(DESTDIR)$(LIBDIR)
+
+.PHONY: install-devel
+install-devel: all install-devel-man3 install-devel-headers install-devel-libs
+
+.PHONY: distclean
+distclean: clean
+       @rm -f TAGS `find . -name '*~' -o -name '.*~'` `find . -name '*.rej'` `find . -name '*.d'` .makefirst
+
+# Rusty's distro magic.
+.PHONY: distrib
+distrib: check distclean delrelease $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2 diff md5sums # nowhitespace
+
+# Makefile must not define:
+# -g -pg -DIPTC_DEBUG
+.PHONY: check
+check:
+       @if echo $(CFLAGS) | egrep -e '-g|-pg|IPTC_DEBUG' >/dev/null; then echo Remove debugging flags; exit 1; else exit 0; fi
+
+.PHONY: nowhitespace
+nowhitespace:
+       @if grep -n '[  ]$$' `find . -name 'Makefile' -o -name '*.[ch]'`; then exit 1; else exit 0; fi
+
+.PHONY: delrelease
+delrelease:
+       rm -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+
+$(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2:
+       cd .. && ln -sf userspace iptables-$(IPTABLES_VERSION) && tar cvf - --exclude CVS iptables-$(IPTABLES_VERSION)/. | bzip2 -9 > $@ && rm iptables-$(IPTABLES_VERSION)
+
+.PHONY: diff
+diff: $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+       @mkdir /tmp/diffdir
+       @cd /tmp/diffdir && tar -x --bzip2 -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+       @set -e; cd /tmp/diffdir; tar -x --bzip2 -f $(RELEASE_DIR)/iptables-$(OLD_IPTABLES_VERSION).tar.bz2; echo Creating patch-iptables-$(OLD_IPTABLES_VERSION)-$(IPTABLES_VERSION).bz2; diff -urN iptables-$(OLD_IPTABLES_VERSION) iptables-$(IPTABLES_VERSION) | bzip2 -9 > $(RELEASE_DIR)/patch-iptables-$(OLD_IPTABLES_VERSION)-$(IPTABLES_VERSION).bz2
+       @rm -rf /tmp/diffdir
+
+.PHONY: md5sums
+md5sums:
+       cd $(RELEASE_DIR)/ && md5sum patch-iptables-*-$(IPTABLES_VERSION).bz2 iptables-$(IPTABLES_VERSION).tar.bz2
+
+# $(wildcard) fails wierdly with make v.3.78.1.
+include $(shell echo */Makefile)
+include Rules.make
diff --git a/extensions/.#libipt_ECN.c.1.7 b/extensions/.#libipt_ECN.c.1.7
new file mode 100644 (file)
index 0000000..7bb0044
--- /dev/null
@@ -0,0 +1,180 @@
+/* Shared library add-on to iptables for ECN, $Version$
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ECN.c borrowed heavily from libipt_DSCP.c
+ *
+ * $Id: libipt_ECN.c,v 1.7 2002/05/29 15:11:36 laforge Exp $
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_ECN.h>
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"ECN target v%s options\n"
+"  --ecn-tcp-remove            Remove all ECN bits from TCP header\n"
+"ECN target v%s EXPERIMENTAL options (use with extreme care!)\n"
+"  --ecn-ip-ect                        Set the IPv4 ECT codepoint (0 to 3)\n"
+"  --ecn-tcp-cwr               Set the IPv4 CWR bit (0 or 1)\n"
+"  --ecn-tcp-ece               Set the IPv4 CWR bit (0 or 1)\n",
+               IPTABLES_VERSION, IPTABLES_VERSION
+);
+}
+
+static struct option opts[] = {
+       { "ecn-tcp-remove", 0, 0, 'F' },
+       { "ecn-tcp-cwr", 1, 0, 'G' },
+       { "ecn-tcp-ece", 1, 0, 'H' },
+       { "ecn-ip-ect", 1, 0, '9' },
+       { 0 }
+};
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       unsigned int result;
+       struct ipt_ECN_info *einfo
+               = (struct ipt_ECN_info *)(*target)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-remove ONCE!");
+               einfo->operation = IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR;
+               einfo->proto.tcp.ece = 0;
+               einfo->proto.tcp.cwr = 0;
+               *flags = 1;
+               break;
+       case 'G':
+               if (*flags & IPT_ECN_OP_SET_CWR)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-cwr ONCE!");
+               if (string_to_number(optarg, 0, 1, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_CWR;
+               einfo->proto.tcp.cwr = result;
+               *flags |= IPT_ECN_OP_SET_CWR;
+               break;
+       case 'H':
+               if (*flags & IPT_ECN_OP_SET_ECE)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-ece ONCE!");
+               if (string_to_number(optarg, 0, 1, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_ECE;
+               einfo->proto.tcp.ece = result;
+               *flags |= IPT_ECN_OP_SET_ECE;
+               break;
+       case '9':
+               if (*flags & IPT_ECN_OP_SET_IP)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-ip-ect ONCE!");
+               if (string_to_number(optarg, 0, 3, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_IP;
+               einfo->ip_ect = (result << IPT_ECN_SHIFT);
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "ECN target: Parameter --ecn-remove is required");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_ECN_info *einfo =
+               (const struct ipt_ECN_info *)target->data;
+
+       printf("ECN ");
+
+       if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)
+           && einfo->proto.tcp.ece == 0
+           && einfo->proto.tcp.cwr == 0)
+               printf("TCP remove ");
+       else {
+               if (einfo->operation & IPT_ECN_OP_SET_ECE)
+                       printf("ECE=%u ", einfo->proto.tcp.ece);
+
+               if (einfo->operation & IPT_ECN_OP_SET_CWR)
+                       printf("CWR=%u ", einfo->proto.tcp.cwr);
+
+               if (einfo->operation & IPT_ECN_OP_SET_IP)
+                       printf("ECT codepoint=%u ", einfo->ip_ect);
+       }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_ECN_info *einfo =
+               (const struct ipt_ECN_info *)target->data;
+
+       if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)
+           && einfo->proto.tcp.ece == 0
+           && einfo->proto.tcp.cwr == 0)
+               printf("--ecn-tcp-remove ");
+       else {
+
+               if (einfo->operation & IPT_ECN_OP_SET_ECE)
+                       printf("--ecn-tcp-ece %d ", einfo->proto.tcp.ece);
+
+               if (einfo->operation & IPT_ECN_OP_SET_CWR)
+                       printf("--ecn-tcp-cwr %d ", einfo->proto.tcp.cwr);
+
+               if (einfo->operation & IPT_ECN_OP_SET_IP)
+                       printf("--ecn-ip-ect %d ", einfo->ip_ect);
+       }
+}
+
+static
+struct iptables_target ecn
+= { NULL,
+    "ECN",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_ECN_info)),
+    IPT_ALIGN(sizeof(struct ipt_ECN_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&ecn);
+}
diff --git a/extensions/.#libipt_recent.c.1.6 b/extensions/.#libipt_recent.c.1.6
new file mode 100644 (file)
index 0000000..cb86597
--- /dev/null
@@ -0,0 +1,229 @@
+/* Shared library add-on to iptables to add recent matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_recent.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"recent v%s options:\n"
+"[!] --set                       Add source address to list, always matches.\n"
+"[!] --rcheck                    Match if source address in list.\n"
+"[!] --update                    Match if source address in list, also update last-seen time.\n"
+"[!] --remove                    Match if source address in list, also removes that address from list.\n"
+"    --seconds seconds           For check and update commands above.\n"
+"                                Specifies that the match will only occur if source address last seen within\n"
+"                                the last 'seconds' seconds.\n"
+"    --hitcount hits             For check and update commands above.\n"
+"                                Specifies that the match will only occur if source address seen hits times.\n"
+"                                May be used in conjunction with the seconds option.\n"
+"    --rttl                      For check and update commands above.\n"
+"                                Specifies that the match will only occur if the source address and the TTL\n"
+"                                match between this packet and the one which was set.\n"
+"                                Useful if you have problems with people spoofing their source address in order\n"
+"                                to DoS you via this module.\n"
+"    --name name                 Name of the recent list to be used.  DEFAULT used if none given.\n"
+"    --rsource                   Save the source address of each packet in the recent list table (default).\n"
+"    --rdest                     Save the destination address of each packet in the recent list table.\n"
+,
+IPTABLES_VERSION);
+
+}
+  
+static struct option opts[] = {
+       { "set", 0, 0, 201 }, 
+       { "rcheck", 0, 0, 202 }, 
+       { "update", 0, 0, 203 },
+       { "seconds", 1, 0, 204 }, 
+       { "hitcount", 1, 0, 205 },
+       { "remove",0, 0, 206 },
+       { "rttl",0, 0, 207 },
+       { "name", 1, 0, 208 },
+       { "rsource", 0, 0, 209 },
+       { "rdest", 0, 0, 210 },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)(match)->data;
+
+       *nfcache |= NFC_UNKNOWN;
+
+       strncpy(info->name, "DEFAULT", 200);
+       info->side = IPT_RECENT_SOURCE;
+}
+
+/* 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_recent_info *info = (struct ipt_recent_info *)(*match)->data;
+       switch (c) {
+               case 201:
+                       if (*flags) 
+                               exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--check' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_SET;
+                       if (invert) 
+                               info->invert = 1;
+                       *flags = 1;
+                       break;
+                       
+               case 202:
+                       if (*flags) 
+                               exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--check' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_CHECK;
+                       if (invert)
+                               info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 203:
+                       if (*flags)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--check' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_UPDATE;
+                       if (invert)
+                               info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 206:
+                       if (*flags)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--check' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_REMOVE;
+                       if (invert)
+                               info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 204:
+                       info->seconds = atoi(optarg);
+                       break;
+
+               case 205:
+                       info->hit_count = atoi(optarg);
+                       break;
+
+               case 207:
+                       info->check_set |= IPT_RECENT_TTL;
+                       break;
+
+               case 208:
+                       strncpy(info->name, optarg, 200);
+                       break;
+
+               case 209:
+                       info->side = IPT_RECENT_SOURCE;
+                       break;
+
+               case 210:
+                       info->side = IPT_RECENT_DEST;
+                       break;
+
+               default:
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified a specific option. */
+static void
+final_check(unsigned int flags)
+{
+
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                       "recent: you must specify one of `--set', `--check' "
+                       "`--update' or `--remove'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)match->data;
+
+       if (info->invert) fputc('!', stdout);
+
+       printf("recent: ");
+       if(info->check_set & IPT_RECENT_SET) printf("SET ");
+       if(info->check_set & IPT_RECENT_CHECK) printf("CHECK ");
+       if(info->check_set & IPT_RECENT_UPDATE) printf("UPDATE ");
+       if(info->check_set & IPT_RECENT_REMOVE) printf("REMOVE ");
+       if(info->seconds) printf("seconds: %d ", info->seconds);
+       if(info->hit_count) printf("hit_count: %d ", info->hit_count);
+       if(info->check_set & IPT_RECENT_TTL) printf("TTL-Match ");
+       if(info->name) printf("name: %s ", info->name);
+       if(info->side == IPT_RECENT_SOURCE) printf("side: source ");
+       if(info->side == IPT_RECENT_DEST) printf("side: dest");
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)match;
+
+       if (info->invert) fputc('!', stdout);
+
+       printf("recent: ");
+       if(info->check_set & IPT_RECENT_SET) printf("SET ");
+       if(info->check_set & IPT_RECENT_CHECK) printf("CHECK ");
+       if(info->check_set & IPT_RECENT_UPDATE) printf("UPDATE ");
+       if(info->check_set & IPT_RECENT_REMOVE) printf("REMOVE ");
+       if(info->seconds) printf("seconds: %d ",info->seconds);
+       if(info->hit_count) printf("hit_count: %d ",info->hit_count);
+       if(info->check_set & IPT_RECENT_TTL) printf("TTL-Match ");
+       if(info->name) printf("name: %s ",info->name);
+       if(info->side == IPT_RECENT_SOURCE) printf("side: source ");
+       if(info->side == IPT_RECENT_DEST) printf("side: dest");
+}
+
+static
+struct iptables_match recent
+= { NULL,
+    "recent",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_recent_info)),
+    IPT_ALIGN(sizeof(struct ipt_recent_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&recent);
+}
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/.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_icmp.c.print_type b/extensions/libipt_icmp.c.print_type
new file mode 100644 (file)
index 0000000..6c7b1c3
--- /dev/null
@@ -0,0 +1,310 @@
+/* Shared library add-on to iptables to add ICMP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* special hack for icmp-type 'any': 
+ * Up to kernel <=2.4.20 the problem was:
+ * '-p icmp ' matches all icmp packets
+ * '-p icmp -m icmp' matches _only_ ICMP type 0 :(
+ * This is now fixed by initializing the field * to icmp type 0xFF
+ * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37
+ */
+
+struct icmp_names {
+       const char *name;
+       u_int8_t type;
+       u_int8_t code_min, code_max;
+};
+
+static const struct icmp_names icmp_codes[] = {
+       { "any", 0xFF, 0, 0xFF },
+       { "echo-reply", 0, 0, 0xFF },
+       /* Alias */ { "pong", 0, 0, 0xFF },
+
+       { "destination-unreachable", 3, 0, 0xFF },
+       {   "network-unreachable", 3, 0, 0 },
+       {   "host-unreachable", 3, 1, 1 },
+       {   "protocol-unreachable", 3, 2, 2 },
+       {   "port-unreachable", 3, 3, 3 },
+       {   "fragmentation-needed", 3, 4, 4 },
+       {   "source-route-failed", 3, 5, 5 },
+       {   "network-unknown", 3, 6, 6 },
+       {   "host-unknown", 3, 7, 7 },
+       {   "network-prohibited", 3, 9, 9 },
+       {   "host-prohibited", 3, 10, 10 },
+       {   "TOS-network-unreachable", 3, 11, 11 },
+       {   "TOS-host-unreachable", 3, 12, 12 },
+       {   "communication-prohibited", 3, 13, 13 },
+       {   "host-precedence-violation", 3, 14, 14 },
+       {   "precedence-cutoff", 3, 15, 15 },
+
+       { "source-quench", 4, 0, 0xFF },
+
+       { "redirect", 5, 0, 0xFF },
+       {   "network-redirect", 5, 0, 0 },
+       {   "host-redirect", 5, 1, 1 },
+       {   "TOS-network-redirect", 5, 2, 2 },
+       {   "TOS-host-redirect", 5, 3, 3 },
+
+       { "echo-request", 8, 0, 0xFF },
+       /* Alias */ { "ping", 8, 0, 0xFF },
+
+       { "router-advertisement", 9, 0, 0xFF },
+
+       { "router-solicitation", 10, 0, 0xFF },
+
+       { "time-exceeded", 11, 0, 0xFF },
+       /* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
+       {   "ttl-zero-during-transit", 11, 0, 0 },
+       {   "ttl-zero-during-reassembly", 11, 1, 1 },
+
+       { "parameter-problem", 12, 0, 0xFF },
+       {   "ip-header-bad", 12, 0, 0 },
+       {   "required-option-missing", 12, 1, 1 },
+
+       { "timestamp-request", 13, 0, 0xFF },
+
+       { "timestamp-reply", 14, 0, 0xFF },
+
+       { "address-mask-request", 17, 0, 0xFF },
+
+       { "address-mask-reply", 18, 0, 0xFF }
+};
+
+static void
+print_icmptypes()
+{
+       unsigned int i;
+       printf("Valid ICMP Types:");
+
+       for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) {
+               if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
+                       if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
+                           && (icmp_codes[i].code_max
+                               == icmp_codes[i-1].code_max))
+                               printf(" (%s)", icmp_codes[i].name);
+                       else
+                               printf("\n   %s", icmp_codes[i].name);
+               }
+               else
+                       printf("\n%s", icmp_codes[i].name);
+       }
+       printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ICMP v%s options:\n"
+" --icmp-type [!] typename     match icmp type\n"
+"                              (or numeric type or type/code)\n"
+"\n", IPTABLES_VERSION);
+       print_icmptypes();
+}
+
+static struct option opts[] = {
+       { "icmp-type", 1, 0, '1' },
+       {0}
+};
+
+static unsigned int
+parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[])
+{
+       unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names);
+       unsigned int match = limit;
+       unsigned int i;
+
+       for (i = 0; i < limit; i++) {
+               if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
+                   == 0) {
+                       if (match != limit)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Ambiguous ICMP type `%s':"
+                                          " `%s' or `%s'?",
+                                          icmptype,
+                                          icmp_codes[match].name,
+                                          icmp_codes[i].name);
+                       match = i;
+               }
+       }
+
+       if (match != limit) {
+               *type = icmp_codes[match].type;
+               code[0] = icmp_codes[match].code_min;
+               code[1] = icmp_codes[match].code_max;
+       } else {
+               char *slash;
+               char buffer[strlen(icmptype) + 1];
+               unsigned int number;
+
+               strcpy(buffer, icmptype);
+               slash = strchr(buffer, '/');
+
+               if (slash)
+                       *slash = '\0';
+
+               if (string_to_number(buffer, 0, 255, &number) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid ICMP type `%s'\n", buffer);
+               *type = number;
+               if (slash) {
+                       if (string_to_number(slash+1, 0, 255, &number) == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid ICMP code `%s'\n",
+                                          slash+1);
+                       code[0] = code[1] = number;
+               } else {
+                       code[0] = 0;
+                       code[1] = 0xFF;
+               }
+       }
+
+       if (code[0] == 0 && code[1] == 0xFF)
+               return NFC_IP_SRC_PT;
+       else return NFC_IP_SRC_PT | NFC_IP_DST_PT;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
+
+       icmpinfo->type = 0xFF;
+       icmpinfo->code[1] = 0xFF;
+}
+
+/* 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_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               *nfcache |= parse_icmp(argv[optind-1],
+                                      &icmpinfo->type,
+                                      icmpinfo->code);
+               if (invert)
+                       icmpinfo->invflags |= IPT_ICMP_INV;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void print_icmptype(u_int8_t type,
+                          u_int8_t code_min, u_int8_t code_max,
+                          int invert,
+                          int numeric)
+{
+       if (!numeric) {
+               unsigned int i;
+
+               for (i = 0;
+                    i < sizeof(icmp_codes)/sizeof(struct icmp_names);
+                    i++) {
+                       if (icmp_codes[i].type == type
+                           && icmp_codes[i].code_min == code_min
+                           && icmp_codes[i].code_max == code_max)
+                               break;
+               }
+
+               if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
+                       printf("%s%s ",
+                              invert ? "!" : "",
+                              icmp_codes[i].name);
+                       return;
+               }
+       }
+
+       if (invert)
+               printf("!");
+
+       printf("type %u", type);
+       if (code_min == 0 && code_max == 0xFF)
+               printf(" ");
+       else if (code_min == code_max)
+               printf(" code %u ", code_min);
+       else
+               printf(" codes %u-%u ", code_min, code_max);
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+       printf("icmp ");
+       print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
+                      icmp->invflags & IPT_ICMP_INV,
+                      numeric);
+
+       if (icmp->invflags & ~IPT_ICMP_INV)
+               printf("Unknown invflags: 0x%X ",
+                      icmp->invflags & ~IPT_ICMP_INV);
+}
+
+/* Saves the match in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+       if (icmp->invflags & IPT_ICMP_INV)
+               printf("! ");
+
+       /* special hack for 'any' case */
+       if (icmp->type == 0xFF) {
+               print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
+                               icmp->invflags & IPT_ICMP_INV, 0);
+       } else {
+               printf("--icmp-type %u", icmp->type);
+               if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
+                       printf("/%u", icmp->code[0]);
+               printf(" ");
+       }
+}
+
+/* Final check; we don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+static
+struct iptables_match icmp
+= { NULL,
+    "icmp",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_icmp)),
+    IPT_ALIGN(sizeof(struct ipt_icmp)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&icmp);
+}
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/ip6tables.c.selinux b/ip6tables.c.selinux
new file mode 100644 (file)
index 0000000..fc4e284
--- /dev/null
@@ -0,0 +1,2300 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <ip6tables.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef IP6T_LIB_DIR
+#define IP6T_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC    0x0001
+#define FMT_NOCOUNTS   0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS    0x0008
+#define FMT_NOTABLE    0x0010
+#define FMT_NOTARGET   0x0020
+#define FMT_VIA                0x0040
+#define FMT_NONEWLINE  0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                       | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE               0x0000U
+#define CMD_INSERT             0x0001U
+#define CMD_DELETE             0x0002U
+#define CMD_DELETE_NUM         0x0004U
+#define CMD_REPLACE            0x0008U
+#define CMD_APPEND             0x0010U
+#define CMD_LIST               0x0020U
+#define CMD_FLUSH              0x0040U
+#define CMD_ZERO               0x0080U
+#define CMD_NEW_CHAIN          0x0100U
+#define CMD_DELETE_CHAIN       0x0200U
+#define CMD_SET_POLICY         0x0400U
+#define CMD_CHECK              0x0800U
+#define CMD_RENAME_CHAIN       0x1000U
+#define NUMBER_OF_CMD  13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+                                'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE       0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_SOURCE     0x00002U
+#define OPT_DESTINATION        0x00004U
+#define OPT_PROTOCOL   0x00008U
+#define OPT_JUMP       0x00010U
+#define OPT_VERBOSE    0x00020U
+#define OPT_EXPANDED   0x00040U
+#define OPT_VIANAMEIN  0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_LINENUMBERS 0x00200U
+#define OPT_COUNTERS   0x00400U
+#define NUMBER_OF_OPT  11
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '3', 'c'};
+
+static struct option original_opts[] = {
+       { "append", 1, 0, 'A' },
+       { "delete", 1, 0,  'D' },
+       { "insert", 1, 0,  'I' },
+       { "replace", 1, 0,  'R' },
+       { "list", 2, 0,  'L' },
+       { "flush", 2, 0,  'F' },
+       { "zero", 2, 0,  'Z' },
+       { "new-chain", 1, 0,  'N' },
+       { "delete-chain", 2, 0,  'X' },
+       { "rename-chain", 1, 0,  'E' },
+       { "policy", 1, 0,  'P' },
+       { "source", 1, 0, 's' },
+       { "destination", 1, 0,  'd' },
+       { "src", 1, 0,  's' }, /* synonym */
+       { "dst", 1, 0,  'd' }, /* synonym */
+       { "protocol", 1, 0,  'p' },
+       { "in-interface", 1, 0, 'i' },
+       { "jump", 1, 0, 'j' },
+       { "table", 1, 0, 't' },
+       { "match", 1, 0, 'm' },
+       { "numeric", 0, 0, 'n' },
+       { "out-interface", 1, 0, 'o' },
+       { "verbose", 0, 0, 'v' },
+       { "exact", 0, 0, 'x' },
+       { "version", 0, 0, 'V' },
+       { "help", 2, 0, 'h' },
+       { "line-numbers", 0, 0, '0' },
+       { "modprobe", 1, 0, 'M' },
+       { "set-counters", 1, 0, 'c' },
+       { 0 }
+};
+
+/* we need this for ip6tables-restore. ip6tables-restore.c sets line to the
+ * current line of the input file, in order to give a more precise error
+ * message. ip6tables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+#ifndef __OPTIMIZE__
+struct ip6t_entry_target *
+ip6t_get_target(struct ip6t_entry *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+       /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  --line */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x',' '},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x'},
+/*CHECK*/     {'x','+','+','+','x',' ','x',' ',' ','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IP6T_INV_SRCIP,
+/* -d */ IP6T_INV_DSTIP,
+/* -p */ IP6T_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IP6T_INV_VIA_IN,
+/* -o */ IP6T_INV_VIA_OUT,
+/*--line*/ 0
+};
+
+const char *program_version;
+const char *program_name;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct ip6tables_match *ip6tables_matches = NULL;
+struct ip6tables_target *ip6tables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries6(const ip6tc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmpv6", IPPROTO_ICMPV6 },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+       unsigned int i;
+
+       if (proto && !nolookup) {
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent)
+                       return pent->p_name;
+       }
+
+       for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+               if (chain_protos[i].num == proto)
+                       return chain_protos[i].name;
+
+       return NULL;
+}
+
+static void
+in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
+{
+       memcpy(dst, src, sizeof(struct in6_addr));
+       /* dst->s6_addr = src->s6_addr; */
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       if (status == PARAMETER_PROBLEM)
+               exit_tryhelp(status);
+       if (status == VERSION_PROBLEM)
+               fprintf(stderr,
+                       "Perhaps iptables or your kernel needs to be upgraded.\n");
+       exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+       if (line != -1)
+               fprintf(stderr, "Error occurred at line: %d\n", line);
+       fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+                       program_name, program_name );
+       exit(status);
+}
+
+void
+exit_printhelp(void)
+{
+       struct ip6tables_match *m = NULL;
+       struct ip6tables_target *t = NULL;
+
+       printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+              program_name, program_version, program_name, program_name,
+              program_name, program_name, program_name, program_name,
+              program_name, program_name);
+
+       printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain          Append to chain\n"
+"  --delete  -D chain          Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"                              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"                              Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"                              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]                List the rules in a chain or all chains\n"
+"  --flush   -F [chain]                Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]                Zero counters in chain or all chains\n"
+"  --new     -N chain          Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]                Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"                              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"                              Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --proto     -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask]\n"
+"                              source specification\n"
+"  --destination -d [!] address[/mask]\n"
+"                              destination specification\n"
+"  --in-interface -i [!] input name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --jump      -j target\n"
+"                              target for rule (may load target extension)\n"
+"  --match     -m match\n"
+"                              extended match (may load extension)\n"
+"  --numeric   -n              numeric output of addresses and ports\n"
+"  --out-interface -o [!] output name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --table     -t table        table to manipulate (default: `filter')\n"
+"  --verbose   -v              verbose mode\n"
+"  --line-numbers              print line numbers when listing\n"
+"  --exact     -x              expand numbers (display exact values)\n"
+/*"[!] --fragment      -f              match second or further fragments only\n"*/
+"  --modprobe=<command>                try to insert modules using this command\n"
+"  --set-counters PKTS BYTES   set the counter during insert/append\n"
+"[!] --version -V              print package version.\n");
+
+       /* Print out any special helps. A user might like to be able to add a --help 
+          to the commandline, and see expected results. So we call help for all 
+          matches & targets */
+       for (t=ip6tables_targets;t;t=t->next) {
+               printf("\n");
+               t->help();
+       }
+       for (m=ip6tables_matches;m;m=m->next) {
+               printf("\n");
+               m->help();
+       }
+       exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 0; j < NUMBER_OF_CMD; j++) {
+                       if (!(command & (1<<j)))
+                               continue;
+
+                       if (!(options & (1<<i))) {
+                               if (commands_v_options[j][i] == '+')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "You need to supply the `-%c' "
+                                                  "option for this command\n",
+                                                  optflags[i]);
+                       } else {
+                               if (commands_v_options[j][i] != 'x')
+                                       legal = 1;
+                               else if (legal == 0)
+                                       legal = -1;
+                       }
+               }
+               if (legal == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Illegal option `-%c' with this command\n",
+                                  optflags[i]);
+       }
+}
+
+static char
+opt2char(int option)
+{
+       const char *ptr;
+       for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+       const char *ptr;
+       for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static void
+add_command(int *cmd, const int newcmd, const int othercmds, int invert)
+{
+       if (invert)
+               exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+       if (*cmd & (~othercmds))
+               exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+                          cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+       *cmd |= newcmd;
+}
+
+int
+check_inverse(const char option[], int *invert, int *optind, int argc)
+{
+       if (option && strcmp(option, "!") == 0) {
+               if (*invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Multiple `!' flags not allowed");
+               *invert = TRUE;
+               if (optind) {
+                       *optind = *optind+1;
+                       if (argc && *optind > argc)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "no argument following `!'");
+               }
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+       void *p;
+
+       if ((p = calloc(count, size)) == NULL) {
+               perror("ip6tables: calloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+       void *p;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("ip6tables: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static char *
+addr_to_numeric(const struct in6_addr *addrp)
+{
+       /* 0000:0000:0000:0000:0000:000.000.000.000
+        * 0000:0000:0000:0000:0000:0000:0000:0000 */
+       static char buf[50+1];
+       return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
+
+static struct in6_addr *
+numeric_to_addr(const char *num)
+{
+       static struct in6_addr ap;
+       int err;
+       if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
+               return &ap;
+#ifdef DEBUG
+       fprintf(stderr, "\nnumeric2addr: %d\n", err);
+#endif
+       return (struct in6_addr *)NULL;
+}
+
+
+static struct in6_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+       struct addrinfo hints;
+        struct addrinfo *res;
+        static struct in6_addr *addr;
+       int err;
+
+       memset(&hints, 0, sizeof(hints));
+        hints.ai_flags=AI_CANONNAME;
+        hints.ai_family=AF_INET6;
+        hints.ai_socktype=SOCK_RAW;
+        hints.ai_protocol=41;
+        hints.ai_next=NULL;
+
+       *naddr = 0;
+        if ( (err=getaddrinfo(name, NULL, &hints, &res)) != 0 ){
+#ifdef DEBUG
+                fprintf(stderr,"Name2IP: %s\n",gai_strerror(err)); 
+#endif
+                return (struct in6_addr *) NULL;
+        } else {
+               if (res->ai_family != AF_INET6 ||
+                   res->ai_addrlen != sizeof(struct sockaddr_in6))
+                       return (struct in6_addr *) NULL;
+
+#ifdef DEBUG
+                fprintf(stderr, "resolved: len=%d  %s ", res->ai_addrlen, 
+                    addr_to_numeric(&(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)));
+#endif
+               /* Get the first element of the address-chain */
+               addr = fw_calloc(1, sizeof(struct in6_addr));
+               in6addrcpy(addr, (struct in6_addr *)
+                       &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr);
+               freeaddrinfo(res);
+               *naddr = 1;
+               return addr;
+       }
+
+       return (struct in6_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in6_addr *addr)
+{
+       struct sockaddr_in6 saddr;
+       int err;
+       static char hostname[NI_MAXHOST];
+
+       memset(&saddr, 0, sizeof(struct sockaddr_in6));
+       in6addrcpy(&(saddr.sin6_addr),(struct in6_addr *)addr);
+       saddr.sin6_family = AF_INET6;
+
+        if ( (err=getnameinfo((struct sockaddr *)&saddr,
+                              sizeof(struct sockaddr_in6),
+                              hostname, sizeof(hostname)-1,
+                              NULL, 0, 0)) != 0 ){
+#ifdef DEBUG
+                fprintf(stderr,"IP2Name: %s\n",gai_strerror(err)); 
+#endif
+                return (char *) NULL;
+        } else {
+#ifdef DEBUG
+               fprintf (stderr, "\naddr2host: %s\n", hostname);
+#endif
+
+               return hostname;
+       }
+
+       return (char *) NULL;
+}
+
+static char *
+mask_to_numeric(const struct in6_addr *addrp)
+{
+       static char buf[50+2];
+       int l = ipv6_prefix_length(addrp);
+       if (l == -1) {
+               strcpy(buf, "/");
+               strcat(buf, addr_to_numeric(addrp));
+               return buf;
+       }
+       sprintf(buf, "/%d", l);
+       return buf;
+}
+
+static struct in6_addr *
+network_to_addr(const char *name)
+{
+       /*      abort();*/
+       /* TODO: not implemented yet, but the exception breaks the
+        *       name resolvation */
+       return (struct in6_addr *)NULL;
+}
+
+static char *
+addr_to_anyname(const struct in6_addr *addr)
+{
+       char *name;
+
+       if ((name = addr_to_host(addr)) != NULL)
+               return name;
+
+       return addr_to_numeric(addr);
+}
+
+/*
+ *     All functions starting with "parse" should succeed, otherwise
+ *     the program fails.
+ *     Most routines return pointers to static data that may change
+ *     between calls to the same or other routines with a few exceptions:
+ *     "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *     return global static data.
+*/
+
+static struct in6_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+       struct in6_addr *addrp, *addrptmp;
+
+       if ((addrptmp = numeric_to_addr(name)) != NULL ||
+           (addrptmp = network_to_addr(name)) != NULL) {
+               addrp = fw_malloc(sizeof(struct in6_addr));
+               in6addrcpy(addrp, addrptmp);
+               *naddrs = 1;
+               return addrp;
+       }
+       if ((addrp = host_to_addr(name, naddrs)) != NULL)
+               return addrp;
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in6_addr *
+parse_mask(char *mask)
+{
+       static struct in6_addr maskaddr;
+       struct in6_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 128 bits */
+               memset(&maskaddr, 0xff, sizeof maskaddr);
+               return &maskaddr;
+       }
+       if ((addrp = numeric_to_addr(mask)) != NULL)
+               return addrp;
+       if (string_to_number(mask, 0, 128, &bits) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", mask);
+       if (bits != 0) {
+               char *p = (char *)&maskaddr;
+               memset(p, 0xff, bits / 8);
+               memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
+               p[bits / 8] = 0xff << (8 - (bits & 7));
+               return &maskaddr;
+       }
+
+       memset(&maskaddr, 0, sizeof maskaddr);
+       return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in6_addr **addrpp,
+                     struct in6_addr *maskp, unsigned int *naddrs)
+{
+       struct in6_addr *addrp;
+       char buf[256];
+       char *p;
+       int i, j, n;
+
+       strncpy(buf, name, sizeof(buf) - 1);
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               addrp = parse_mask(p + 1);
+       } else
+               addrp = parse_mask(NULL);
+       in6addrcpy(maskp, addrp);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (!memcmp(maskp, &in6addr_any, sizeof(in6addr_any)))
+               strcpy(buf, "::");
+
+       addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+       n = *naddrs;
+       for (i = 0, j = 0; i < n; i++) {
+               int k;
+               for (k = 0; k < 4; k++)
+                       addrp[j].in6_u.u6_addr32[k] &= maskp->in6_u.u6_addr32[k];
+               j++;
+               for (k = 0; k < j - 1; k++) {
+                       if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) {
+                               (*naddrs)--;
+                               j--;
+                               break;
+                       }
+               }
+       }
+}
+
+struct ip6tables_match *
+find_match(const char *name, enum ip6t_tryload tryload)
+{
+       struct ip6tables_match *ptr;
+       int icmphack = 0;
+  
+       /* This is ugly as hell. Nonetheless, there is no way of changing
+        * this without hurting backwards compatibility */
+       if ( (strcmp(name,"icmpv6") == 0) ||
+            (strcmp(name,"ipv6-icmp") == 0) ||
+            (strcmp(name,"icmp6") == 0) ) icmphack = 1;
+       if (!icmphack) {
+               for (ptr = ip6tables_matches; ptr; ptr = ptr->next) {
+                       if (strcmp(name, ptr->name) == 0)
+                               break;
+               }
+       } else {
+               for (ptr = ip6tables_matches; ptr; ptr = ptr->next) {
+                       if (strcmp("icmp6", ptr->name) == 0)
+                               break;
+               }
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IP6T_LIB_DIR) + sizeof("/libip6t_.so")
+                        + strlen(name)];
+               if (!icmphack)
+                       sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", name);
+               else
+                       sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", "icmpv6");
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified target as match. */
+                       ptr = find_match(name, DONT_LOAD);
+
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load match `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load match `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find match `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+static struct ip6tables_match *
+find_proto(const char *pname, enum ip6t_tryload tryload, int nolookup)
+{
+       unsigned int proto;
+
+       if (string_to_number(pname, 0, 255, &proto) != -1) {
+               char *protoname = proto_to_name(proto, nolookup);
+
+               if (protoname)
+                       return find_match(protoname, tryload);
+       } else
+               return find_match(pname, tryload);
+
+       return NULL;
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+       unsigned int proto;
+
+       if (string_to_number(s, 0, 255, &proto) == -1) {
+               struct protoent *pent;
+
+               if ((pent = getprotobyname(s)))
+                       proto = pent->p_proto;
+               else {
+                       unsigned int i;
+                       for (i = 0;
+                            i < sizeof(chain_protos)/sizeof(struct pprot);
+                            i++) {
+                               if (strcmp(s, chain_protos[i].name) == 0) {
+                                       proto = chain_protos[i].num;
+                                       break;
+                               }
+                       }
+                       if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unknown protocol `%s' specified",
+                                          s);
+               }
+       }
+
+       return (u_int16_t)proto;
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if (vialen == 0)
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (!isalnum(vianame[i]) 
+                           && vianame[i] != '_' 
+                           && vianame[i] != '.') {
+                               printf("Warning: wierd character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+       unsigned int rulenum;
+
+       if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid rule number `%s'", rule);
+
+       return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > sizeof(ip6t_chainlabel))
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name `%s' (%i chars max)",
+                          targetname, sizeof(ip6t_chainlabel)-1);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+                unsigned int *ret)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+          int invert)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+
+       if (invert) {
+               unsigned int i;
+               for (i = 0; 1 << i != option; i++);
+
+               if (!inverse_for_options[i])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot have ! before -%c",
+                                  opt2char(option));
+               *invflg |= inverse_for_options[i];
+       }
+}
+
+struct ip6tables_target *
+find_target(const char *name, enum ip6t_tryload tryload)
+{
+       struct ip6tables_target *ptr;
+
+       /* Standard target? */
+       if (strcmp(name, "") == 0
+           || strcmp(name, IP6TC_LABEL_ACCEPT) == 0
+           || strcmp(name, IP6TC_LABEL_DROP) == 0
+           || strcmp(name, IP6TC_LABEL_QUEUE) == 0
+           || strcmp(name, IP6TC_LABEL_RETURN) == 0)
+               name = "standard";
+
+       for (ptr = ip6tables_targets; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IP6T_LIB_DIR) + sizeof("/libip6t_.so")
+                        + strlen(name)];
+               sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified match as a target. */
+                       ptr = find_target(name, DONT_LOAD);
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load target `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load target `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find target `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+             unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+void
+register_match6(struct ip6tables_match *me)
+{
+       struct ip6tables_match **i;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_match(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: match `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IP6T_ALIGN(me->size)) {
+               fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Append to list. */
+       for (i = &ip6tables_matches; *i; i = &(*i)->next);
+       me->next = NULL;
+       *i = me;
+
+       me->m = NULL;
+       me->mflags = 0;
+}
+
+void
+register_target6(struct ip6tables_target *me)
+{
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_target(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: target `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IP6T_ALIGN(me->size)) {
+               fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Prepend to list. */
+       me->next = ip6tables_targets;
+       ip6tables_targets = me;
+       me->t = NULL;
+       me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+       if (format & FMT_KILOMEGAGIGA) {
+               if (number > 99999) {
+                       number = (number + 500) / 1000;
+                       if (number > 9999) {
+                               number = (number + 500) / 1000;
+                               if (number > 9999) {
+                                       number = (number + 500) / 1000;
+                                       if (number > 9999) {
+                                               number = (number + 500) / 1000;
+                                               printf(FMT("%4lluT ","%lluT "), number);
+                                       }
+                                       else printf(FMT("%4lluG ","%lluG "), number);
+                               }
+                               else printf(FMT("%4lluM ","%lluM "), number);
+                       } else
+                               printf(FMT("%4lluK ","%lluK "), number);
+               } else
+                       printf(FMT("%5llu ","%llu "), number);
+       } else
+               printf(FMT("%8llu ","%llu "), number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, ip6tc_handle_t *handle)
+{
+       struct ip6t_counters counters;
+       const char *pol = ip6tc_get_policy(chain, &counters, handle);
+       printf("Chain %s", chain);
+       if (pol) {
+               printf(" (policy %s", pol);
+               if (!(format & FMT_NOCOUNTS)) {
+                       fputc(' ', stdout);
+                       print_num(counters.pcnt, (format|FMT_NOTABLE));
+                       fputs("packets, ", stdout);
+                       print_num(counters.bcnt, (format|FMT_NOTABLE));
+                       fputs("bytes", stdout);
+               }
+               printf(")\n");
+       } else {
+               unsigned int refs;
+               if (!ip6tc_get_references(&refs, chain, handle))
+                       printf(" (ERROR obtaining refs)\n");
+               else
+                       printf(" (%u references)\n", refs);
+       }
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4s ", "%s "), "num");
+       if (!(format & FMT_NOCOUNTS)) {
+               if (format & FMT_KILOMEGAGIGA) {
+                       printf(FMT("%5s ","%s "), "pkts");
+                       printf(FMT("%5s ","%s "), "bytes");
+               } else {
+                       printf(FMT("%8s ","%s "), "pkts");
+                       printf(FMT("%10s ","%s "), "bytes");
+               }
+       }
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ","%s "), "target");
+       fputs(" prot ", stdout);
+       if (format & FMT_OPTIONS)
+               fputs("opt", stdout);
+       if (format & FMT_VIA) {
+               printf(FMT(" %-6s ","%s "), "in");
+               printf(FMT("%-6s ","%s "), "out");
+       }
+       printf(FMT(" %-19s ","%s "), "source");
+       printf(FMT(" %-19s "," %s "), "destination");
+       printf("\n");
+}
+
+
+static int
+print_match(const struct ip6t_entry_match *m,
+           const struct ip6t_ip6 *ip,
+           int numeric)
+{
+       struct ip6tables_match *match = find_match(m->u.user.name, TRY_LOAD);
+
+       if (match) {
+               if (match->print)
+                       match->print(ip, m, numeric);
+               else
+                       printf("%s ", match->name);
+       } else {
+               if (m->u.user.name[0])
+                       printf("UNKNOWN match `%s' ", m->u.user.name);
+       }
+       /* Don't stop iterating. */
+       return 0;
+}
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct ip6t_entry *fw,
+              const char *targname,
+              unsigned int num,
+              unsigned int format,
+              const ip6tc_handle_t handle)
+{
+       struct ip6tables_target *target = NULL;
+       const struct ip6t_entry_target *t;
+       u_int8_t flags;
+       char buf[BUFSIZ];
+
+       if (!ip6tc_is_chain(targname, handle))
+               target = find_target(targname, TRY_LOAD);
+       else
+               target = find_target(IP6T_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+       t = ip6t_get_target((struct ip6t_entry *)fw);
+       flags = fw->ipv6.flags;
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4u ", "%u "), num+1);
+
+       if (!(format & FMT_NOCOUNTS)) {
+               print_num(fw->counters.pcnt, format);
+               print_num(fw->counters.bcnt, format);
+       }
+
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ", "%s "), targname);
+
+       fputc(fw->ipv6.invflags & IP6T_INV_PROTO ? '!' : ' ', stdout);
+       {
+               char *pname = proto_to_name(fw->ipv6.proto, format&FMT_NUMERIC);
+               if (pname)
+                       printf(FMT("%-5s", "%s "), pname);
+               else
+                       printf(FMT("%-5hu", "%hu "), fw->ipv6.proto);
+       }
+
+       if (format & FMT_OPTIONS) {
+               if (format & FMT_NOTABLE)
+                       fputs("opt ", stdout);
+               fputc(' ', stdout); /* Invert flag of FRAG */
+               fputc(' ', stdout); /* -f */
+               fputc(' ', stdout);
+       }
+
+       if (format & FMT_VIA) {
+               char iface[IFNAMSIZ+2];
+
+               if (fw->ipv6.invflags & IP6T_INV_VIA_IN) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ipv6.iniface[0] != '\0') {
+                       strcat(iface, fw->ipv6.iniface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT(" %-6s ","in %s "), iface);
+
+               if (fw->ipv6.invflags & IP6T_INV_VIA_OUT) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ipv6.outiface[0] != '\0') {
+                       strcat(iface, fw->ipv6.outiface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT("%-6s ","out %s "), iface);
+       }
+
+       fputc(fw->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout);
+       if (!memcmp(&fw->ipv6.smsk, &in6addr_any, sizeof in6addr_any) 
+           && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","%s "), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_numeric(&(fw->ipv6.src)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ipv6.src)));
+               strcat(buf, mask_to_numeric(&(fw->ipv6.smsk)));
+               printf(FMT("%-19s ","%s "), buf);
+       }
+
+       fputc(fw->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout);
+       if (!memcmp(&fw->ipv6.dmsk, &in6addr_any, sizeof in6addr_any)
+           && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s","-> %s"), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_numeric(&(fw->ipv6.dst)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ipv6.dst)));
+               strcat(buf, mask_to_numeric(&(fw->ipv6.dmsk)));
+               printf(FMT("%-19s","-> %s"), buf);
+       }
+
+       if (format & FMT_NOTABLE)
+               fputs("  ", stdout);
+
+       IP6T_MATCH_ITERATE(fw, print_match, &fw->ipv6, format & FMT_NUMERIC);
+
+       if (target) {
+               if (target->print)
+                       /* Print the target information. */
+                       target->print(&fw->ipv6, t, format & FMT_NUMERIC);
+       } else if (t->u.target_size != sizeof(*t))
+               printf("[%u bytes of unknown target data] ",
+                      t->u.target_size - sizeof(*t));
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ip6t_entry *fw,
+                   const ip6tc_handle_t h)
+{
+       struct ip6t_entry_target *t;
+
+       t = ip6t_get_target((struct ip6t_entry *)fw);
+       print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_append_entry(chain, fw, handle);
+               }
+       }
+
+       return ret;
+}
+
+static int
+replace_entry(const ip6t_chainlabel chain,
+             struct ip6t_entry *fw,
+             unsigned int rulenum,
+             const struct in6_addr *saddr,
+             const struct in6_addr *daddr,
+             int verbose,
+             ip6tc_handle_t *handle)
+{
+       fw->ipv6.src = *saddr;
+       fw->ipv6.dst = *daddr;
+
+       if (verbose)
+               print_firewall_line(fw, *handle);
+       return ip6tc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int rulenum,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_insert_entry(chain, fw, rulenum, handle);
+               }
+       }
+
+       return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct ip6t_entry *fw)
+{
+       /* Establish mask for comparison */
+       unsigned int size;
+       struct ip6tables_match *m;
+       unsigned char *mask, *mptr;
+
+       size = sizeof(struct ip6t_entry);
+       for (m = ip6tables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + m->size;
+       }
+
+       mask = fw_calloc(1, size
+                        + IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+                        + ip6tables_targets->size);
+
+       memset(mask, 0xFF, sizeof(struct ip6t_entry));
+       mptr = mask + sizeof(struct ip6t_entry);
+
+       for (m = ip6tables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memset(mptr, 0xFF,
+                      IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                      + m->userspacesize);
+               mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + m->size;
+       }
+
+       memset(mptr, 0xFF, 
+              IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+              + ip6tables_targets->userspacesize);
+
+       return mask;
+}
+
+static int
+delete_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+       unsigned char *mask;
+
+       mask = make_delete_mask(fw);
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_delete_entry(chain, fw, mask, handle);
+               }
+       }
+       return ret;
+}
+
+int
+for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *),
+              int verbose, int builtinstoo, ip6tc_handle_t *handle)
+{
+        int ret = 1;
+       const char *chain;
+       char *chains;
+       unsigned int i, chaincount = 0;
+
+       chain = ip6tc_first_chain(handle);
+       while (chain) {
+               chaincount++;
+               chain = ip6tc_next_chain(handle);
+        }
+
+       chains = fw_malloc(sizeof(ip6t_chainlabel) * chaincount);
+       i = 0;
+       chain = ip6tc_first_chain(handle);
+       while (chain) {
+               strcpy(chains + i*sizeof(ip6t_chainlabel), chain);
+               i++;
+               chain = ip6tc_next_chain(handle);
+        }
+
+       for (i = 0; i < chaincount; i++) {
+               if (!builtinstoo
+                   && ip6tc_builtin(chains + i*sizeof(ip6t_chainlabel),
+                                   *handle))
+                       continue;
+               ret &= fn(chains + i*sizeof(ip6t_chainlabel), verbose, handle);
+       }
+
+       free(chains);
+        return ret;
+}
+
+int
+flush_entries(const ip6t_chainlabel chain, int verbose,
+             ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(flush_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Flushing chain `%s'\n", chain);
+       return ip6tc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const ip6t_chainlabel chain, int verbose,
+            ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(zero_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Zeroing chain `%s'\n", chain);
+       return ip6tc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const ip6t_chainlabel chain, int verbose,
+            ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(delete_chain, verbose, 0, handle);
+
+       if (verbose)
+               fprintf(stdout, "Deleting chain `%s'\n", chain);
+       return ip6tc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ip6t_chainlabel chain, int verbose, int numeric,
+            int expanded, int linenumbers, ip6tc_handle_t *handle)
+{
+       int found = 0;
+       unsigned int format;
+       const char *this;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       for (this = ip6tc_first_chain(handle);
+            this;
+            this = ip6tc_next_chain(handle)) {
+               const struct ip6t_entry *i;
+               unsigned int num;
+
+               if (chain && strcmp(chain, this) != 0)
+                       continue;
+
+               if (found) printf("\n");
+
+               print_header(format, this, handle);
+               i = ip6tc_first_rule(this, handle);
+
+               num = 0;
+               while (i) {
+                       print_firewall(i,
+                                      ip6tc_get_target(i, handle),
+                                      num++,
+                                      format,
+                                      *handle);
+                       i = ip6tc_next_rule(i, handle);
+               }
+               found = 1;
+       }
+
+       errno = ENOENT;
+       return found;
+}
+
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = malloc(1024);
+       if (ret) {
+               switch (read(procfile, ret, 1024)) {
+               case -1: goto fail;
+               case 1024: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+int ip6tables_insmod(const char *modname, const char *modprobe)
+{
+       char *buf = NULL;
+       char *argv[3];
+
+       /* If they don't explicitly set it, read out of kernel */
+       if (!modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+
+       switch (fork()) {
+       case 0:
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               argv[2] = NULL;
+               execv(argv[0], argv);
+
+               /* not usually reached */
+               exit(0);
+       case -1:
+               return -1;
+
+       default: /* parent */
+               wait(NULL);
+       }
+
+       free(buf);
+       return 0;
+}
+
+static struct ip6t_entry *
+generate_entry(const struct ip6t_entry *fw,
+              struct ip6tables_match *matches,
+              struct ip6t_entry_target *target)
+{
+       unsigned int size;
+       struct ip6tables_match *m;
+       struct ip6t_entry *e;
+
+       size = sizeof(struct ip6t_entry);
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += m->m->u.match_size;
+       }
+
+       e = fw_malloc(size + target->u.target_size);
+       *e = *fw;
+       e->target_offset = size;
+       e->next_offset = size + target->u.target_size;
+
+       size = 0;
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memcpy(e->elems + size, m->m, m->m->u.match_size);
+               size += m->m->u.match_size;
+       }
+       memcpy(e->elems + size, target, target->u.target_size);
+
+       return e;
+}
+
+int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
+{
+       struct ip6t_entry fw, *e = NULL;
+       int invert = 0;
+       unsigned int nsaddrs = 0, ndaddrs = 0;
+       struct in6_addr *saddrs = NULL, *daddrs = NULL;
+
+       int c, verbose = 0;
+       const char *chain = NULL;
+       const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+       const char *policy = NULL, *newname = NULL;
+       unsigned int rulenum = 0, options = 0, command = 0;
+       const char *pcnt = NULL, *bcnt = NULL;
+       int ret = 1;
+       struct ip6tables_match *m;
+       struct ip6tables_target *target = NULL;
+       struct ip6tables_target *t;
+       const char *jumpto = "";
+       char *protocol = NULL;
+       const char *modprobe = NULL;
+       int proto_used = 0;
+       char icmp6p[] = "icmpv6";
+
+       memset(&fw, 0, sizeof(fw));
+
+       opts = original_opts;
+       global_option_offset = 0;
+
+       /* re-set optind to 0 in case do_command gets called
+        * a second time */
+       optind = 0;
+
+       /* clear mflags in case do_command gets called a second time
+        * (we clear the global list of all matches for security)*/
+       for (m = ip6tables_matches; m; m = m->next) {
+               m->mflags = 0;
+               m->used = 0;
+       }
+
+       for (t = ip6tables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+          "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:",
+                                          opts, NULL)) != -1) {
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'A':
+                       add_command(&command, CMD_APPEND, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       add_command(&command, CMD_DELETE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!') {
+                               rulenum = parse_rulenumber(argv[optind++]);
+                               command = CMD_DELETE_NUM;
+                       }
+                       break;
+
+               case 'R':
+                       add_command(&command, CMD_REPLACE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a rule number",
+                                          cmd2char(CMD_REPLACE));
+                       break;
+
+               case 'I':
+                       add_command(&command, CMD_INSERT, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else rulenum = 1;
+                       break;
+
+               case 'L':
+                       add_command(&command, CMD_LIST, CMD_ZERO,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'F':
+                       add_command(&command, CMD_FLUSH, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'Z':
+                       add_command(&command, CMD_ZERO, CMD_LIST,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                               && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'N':
+                       if (optarg && *optarg == '-')
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name not allowed to start "
+                                          "with `-'\n");
+                       if (find_target(optarg, TRY_LOAD))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name may not clash "
+                                          "with target name\n");
+                       add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'X':
+                       add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'E':
+                       add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               newname = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires old-chain-name and "
+                                          "new-chain-name",
+                                           cmd2char(CMD_RENAME_CHAIN));
+                       break;
+
+               case 'P':
+                       add_command(&command, CMD_SET_POLICY, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               policy = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a chain and a policy",
+                                          cmd2char(CMD_SET_POLICY));
+                       break;
+
+               case 'h':
+                       if (!optarg)
+                               optarg = argv[optind];
+
+                       /* iptables -p icmp -h */
+                       if (!ip6tables_matches && protocol)
+                               find_match(protocol, TRY_LOAD);
+
+                       exit_printhelp();
+
+                       /*
+                        * Option selection
+                        */
+               case 'p':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_PROTOCOL, &fw.ipv6.invflags,
+                                  invert);
+
+                       /* Canonicalize into lower case */
+                       for (protocol = argv[optind-1]; *protocol; protocol++)
+                               *protocol = tolower(*protocol);
+
+                       protocol = argv[optind-1];
+                       if ( strcmp(protocol,"ipv6-icmp") == 0)
+                               protocol = icmp6p;
+                       fw.ipv6.proto = parse_protocol(protocol);
+                       fw.ipv6.flags |= IP6T_F_PROTO;
+
+                       if (fw.ipv6.proto == 0
+                           && (fw.ipv6.invflags & IP6T_INV_PROTO))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "rule would never match protocol");
+                       fw.nfcache |= NFC_IP6_PROTO;
+                       break;
+
+               case 's':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_SOURCE, &fw.ipv6.invflags,
+                                  invert);
+                       shostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP6_SRC;
+                       break;
+
+               case 'd':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_DESTINATION, &fw.ipv6.invflags,
+                                  invert);
+                       dhostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP6_DST;
+                       break;
+
+               case 'j':
+                       set_option(&options, OPT_JUMP, &fw.ipv6.invflags,
+                                  invert);
+                       jumpto = parse_target(optarg);
+                       /* TRY_LOAD (may be chain name) */
+                       target = find_target(jumpto, TRY_LOAD);
+
+                       if (target) {
+                               size_t size;
+
+                               size = IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+                                       + target->size;
+
+                               target->t = fw_calloc(1, size);
+                               target->t->u.target_size = size;
+                               strcpy(target->t->u.user.name, jumpto);
+                               target->init(target->t, &fw.nfcache);
+                               opts = merge_options(opts, target->extra_opts, &target->option_offset);
+                       }
+                       break;
+
+
+               case 'i':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEIN, &fw.ipv6.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ipv6.iniface,
+                                       fw.ipv6.iniface_mask);
+                       fw.nfcache |= NFC_IP6_IF_IN;
+                       break;
+
+               case 'o':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEOUT, &fw.ipv6.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ipv6.outiface,
+                                       fw.ipv6.outiface_mask);
+                       fw.nfcache |= NFC_IP6_IF_OUT;
+                       break;
+
+               case 'v':
+                       if (!verbose)
+                               set_option(&options, OPT_VERBOSE,
+                                          &fw.ipv6.invflags, invert);
+                       verbose++;
+                       break;
+
+               case 'm': {
+                       size_t size;
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --match");
+
+                       m = find_match(optarg, LOAD_MUST_SUCCEED);
+                       size = IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                                        + m->size;
+                       m->m = fw_calloc(1, size);
+                       m->m->u.match_size = size;
+                       strcpy(m->m->u.user.name, m->name);
+                       m->init(m->m, &fw.nfcache);
+                       opts = merge_options(opts, m->extra_opts, &m->option_offset);
+               }
+               break;
+
+               case 'n':
+                       set_option(&options, OPT_NUMERIC, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 't':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --table");
+                       *table = argv[optind-1];
+                       break;
+
+               case 'x':
+                       set_option(&options, OPT_EXPANDED, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 'V':
+                       if (invert)
+                               printf("Not %s ;-)\n", program_version);
+                       else
+                               printf("%s v%s\n",
+                                      program_name, program_version);
+                       exit(0);
+
+               case '0':
+                       set_option(&options, OPT_LINENUMBERS, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 'M':
+                       modprobe = optarg;
+                       break;
+
+               case 'c':
+
+                       set_option(&options, OPT_COUNTERS, &fw.ipv6.invflags,
+                                  invert);
+                       pcnt = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               bcnt = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c requires packet and byte counter",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c packet counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c byte counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+                       
+                       break;
+
+
+               case 1: /* non option */
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "multiple consecutive ! not"
+                                                  " allowed");
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+
+               default:
+                       /* FIXME: This scheme doesn't allow two of the same
+                          matches --RR */
+                       if (!target
+                           || !(target->parse(c - target->option_offset,
+                                              argv, invert,
+                                              &target->tflags,
+                                              &fw, &target->t))) {
+                               for (m = ip6tables_matches; m; m = m->next) {
+                                       if (!m->used)
+                                               continue;
+
+                                       if (m->parse(c - m->option_offset,
+                                                    argv, invert,
+                                                    &m->mflags,
+                                                    &fw,
+                                                    &fw.nfcache,
+                                                    &m->m))
+                                               break;
+                               }
+
+                               /* If you listen carefully, you can
+                                  actually hear this code suck. */
+
+                               /* some explanations (after four different bugs
+                                * in 3 different releases): If we encountere a
+                                * parameter, that has not been parsed yet,
+                                * it's not an option of an explicitly loaded
+                                * match or a target.  However, we support
+                                * implicit loading of the protocol match
+                                * extension.  '-p tcp' means 'l4 proto 6' and
+                                * at the same time 'load tcp protocol match on
+                                * demand if we specify --dport'.
+                                *
+                                * To make this work, we need to make sure:
+                                * - the parameter has not been parsed by
+                                *   a match (m above)
+                                * - a protocol has been specified
+                                * - the protocol extension has not been
+                                *   loaded yet, or is loaded and unused
+                                *   [think of iptables-restore!]
+                                * - the protocol extension can be successively
+                                *   loaded
+                                */
+                               if (m == NULL
+                                   && protocol
+                                   && (!find_proto(protocol, DONT_LOAD,
+                                                  options&OPT_NUMERIC) 
+                                       || (find_proto(protocol, DONT_LOAD,
+                                                       options&OPT_NUMERIC)
+                                           && (proto_used == 0))
+                                      )
+                                   && (m = find_proto(protocol, TRY_LOAD,
+                                                      options&OPT_NUMERIC))) {
+                                       /* Try loading protocol */
+                                       size_t size;
+                                       
+                                       proto_used = 1;
+
+                                       size = IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                                                        + m->size;
+
+                                       m->m = fw_calloc(1, size);
+                                       m->m->u.match_size = size;
+                                       strcpy(m->m->u.user.name, m->name);
+                                       m->init(m->m, &fw.nfcache);
+
+                                       opts = merge_options(opts,
+                                           m->extra_opts, &m->option_offset);
+
+                                       optind--;
+                                       continue;
+                               }
+
+                               if (!m)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind-1]);
+                       }
+               }
+               invert = FALSE;
+       }
+
+       for (m = ip6tables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               m->final_check(m->mflags);
+       }
+
+       if (target)
+               target->final_check(target->tflags);
+
+       /* Fix me: must put inverse options checking here --MN */
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "nothing appropriate following !");
+
+       if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+               if (!(options & OPT_DESTINATION))
+                       dhostnetworkmask = "::0/0";
+               if (!(options & OPT_SOURCE))
+                       shostnetworkmask = "::0/0";
+       }
+
+       if (shostnetworkmask)
+               parse_hostnetworkmask(shostnetworkmask, &saddrs,
+                                     &(fw.ipv6.smsk), &nsaddrs);
+
+       if (dhostnetworkmask)
+               parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+                                     &(fw.ipv6.dmsk), &ndaddrs);
+
+       if ((nsaddrs > 1 || ndaddrs > 1) &&
+           (fw.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP)))
+               exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+                          " source or destination IP addresses");
+
+       if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+               exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+                          "specify a unique address");
+
+       generic_opt_check(command, options);
+
+       if (chain && strlen(chain) > IP6T_FUNCTION_MAXNAMELEN)
+               exit_error(PARAMETER_PROBLEM,
+                          "chain name `%s' too long (must be under %i chars)",
+                          chain, IP6T_FUNCTION_MAXNAMELEN);
+
+       /* only allocate handle if we weren't called with a handle */
+       if (!*handle)
+               *handle = ip6tc_init(*table);
+
+       if (!*handle) {
+               /* try to insmod the module if iptc_init failed */
+               ip6tables_insmod("ip6_tables", modprobe);
+               *handle = ip6tc_init(*table);
+       }
+
+       if (!*handle)
+               exit_error(VERSION_PROBLEM,
+                       "can't initialize ip6tables table `%s': %s",
+                       *table, ip6tc_strerror(errno));
+
+       if (command == CMD_APPEND
+           || command == CMD_DELETE
+           || command == CMD_INSERT
+           || command == CMD_REPLACE) {
+               if (strcmp(chain, "PREROUTING") == 0
+                   || strcmp(chain, "INPUT") == 0) {
+                       /* -o not valid with incoming packets. */
+                       if (options & OPT_VIANAMEOUT)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEOUT),
+                                          chain);
+               }
+
+               if (strcmp(chain, "POSTROUTING") == 0
+                   || strcmp(chain, "OUTPUT") == 0) {
+                       /* -i not valid with outgoing packets */
+                       if (options & OPT_VIANAMEIN)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEIN),
+                                          chain);
+               }
+
+               if (target && ip6tc_is_chain(jumpto, *handle)) {
+                       printf("Warning: using chain %s, not extension\n",
+                              jumpto);
+
+                       target = NULL;
+               }
+
+               /* If they didn't specify a target, or it's a chain
+                  name, use standard. */
+               if (!target
+                   && (strlen(jumpto) == 0
+                       || ip6tc_is_chain(jumpto, *handle))) {
+                       size_t size;
+
+                       target = find_target(IP6T_STANDARD_TARGET,
+                                            LOAD_MUST_SUCCEED);
+
+                       size = sizeof(struct ip6t_entry_target)
+                               + target->size;
+                       target->t = fw_calloc(1, size);
+                       target->t->u.target_size = size;
+                       strcpy(target->t->u.user.name, jumpto);
+                       target->init(target->t, &fw.nfcache);
+               }
+
+               if (!target) {
+                       /* it is no chain, and we can't load a plugin.
+                        * We cannot know if the plugin is corrupt, non
+                        * existant OR if the user just misspelled a
+                        * chain. */
+                       find_target(jumpto, LOAD_MUST_SUCCEED);
+               } else {
+                       e = generate_entry(&fw, ip6tables_matches, target->t);
+               }
+       }
+
+       switch (command) {
+       case CMD_APPEND:
+               ret = append_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE:
+               ret = delete_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE_NUM:
+               ret = ip6tc_delete_num_entry(chain, rulenum - 1, handle);
+               break;
+       case CMD_REPLACE:
+               ret = replace_entry(chain, e, rulenum - 1,
+                                   saddrs, daddrs, options&OPT_VERBOSE,
+                                   handle);
+               break;
+       case CMD_INSERT:
+               ret = insert_entry(chain, e, rulenum - 1,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_LIST:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               break;
+       case CMD_FLUSH:
+               ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_ZERO:
+               ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_LIST|CMD_ZERO:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               if (ret)
+                       ret = zero_entries(chain,
+                                          options&OPT_VERBOSE, handle);
+               break;
+       case CMD_NEW_CHAIN:
+               ret = ip6tc_create_chain(chain, handle);
+               break;
+       case CMD_DELETE_CHAIN:
+               ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_RENAME_CHAIN:
+               ret = ip6tc_rename_chain(chain, newname,        handle);
+               break;
+       case CMD_SET_POLICY:
+               ret = ip6tc_set_policy(chain, policy, NULL, handle);
+               break;
+       default:
+               /* We should never reach this... */
+               exit_tryhelp(2);
+       }
+
+       if (verbose > 1)
+               dump_entries6(*handle);
+
+       return ret;
+}
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-restore.c.counters b/iptables-restore.c.counters
new file mode 100644 (file)
index 0000000..b40630c
--- /dev/null
@@ -0,0 +1,384 @@
+/* Code to restore the iptables state, from file by iptables-save. 
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ * $Id: iptables-restore.c,v 1.26 2003/05/02 15:30:11 laforge Exp $
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "iptables.h"
+#include "libiptc/libiptc.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets.  */
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "verbose", 1, 0, 'v' },
+       { "help", 0, 0, 'h' },
+       { "noflush", 0, 0, 'n'},
+       { "modprobe", 1, 0, 'M'},
+       { 0 }
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+static void print_usage(const char *name, const char *version)
+{
+       fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-h]\n"
+                       "          [ --binary ]\n"
+                       "          [ --counters ]\n"
+                       "          [ --verbose ]\n"
+                       "          [ --help ]\n"
+                       "          [ --noflush ]\n"
+                       "          [ --modprobe=<command>]\n", name);
+               
+       exit(1);
+}
+
+iptc_handle_t create_handle(const char *tablename, const char* modprobe )
+{
+       iptc_handle_t handle;
+
+       handle = iptc_init(tablename);
+
+       if (!handle) {
+               /* try to insmod the module if iptc_init failed */
+               iptables_insmod("ip_tables", modprobe);
+               handle = iptc_init(tablename);
+       }
+
+       if (!handle) {
+               exit_error(PARAMETER_PROBLEM, "%s: unable to initialize"
+                       "table '%s'\n", program_name, tablename);
+               exit(1);
+       }
+       return handle;
+}
+
+int parse_counters(char *string, struct ipt_counters *ctr)
+{
+       return (sscanf(string, "[%llu:%llu]", &ctr->pcnt, &ctr->bcnt) == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+       DEBUGP("add_argv: %s\n", what);
+       if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) {
+               newargv[newargc] = strdup(what);
+               newargc++;
+               return 1;
+       } else 
+               return 0;
+}
+
+static void free_argv(void) {
+       int i;
+
+       for (i = 0; i < newargc; i++)
+               free(newargv[i]);
+}
+
+int main(int argc, char *argv[])
+{
+       iptc_handle_t handle = NULL;
+       char buffer[10240];
+       int c;
+       char curtable[IPT_TABLE_MAXNAMELEN + 1];
+       FILE *in;
+       const char *modprobe = 0;
+       int in_table = 0;
+
+       program_name = "iptables-restore";
+       program_version = IPTABLES_VERSION;
+       line = 0;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcvhnM:", options, NULL)) != -1) {
+               switch (c) {
+                       case 'b':
+                               binary = 1;
+                               break;
+                       case 'c':
+                               counters = 1;
+                               break;
+                       case 'v':
+                               verbose = 1;
+                               break;
+                       case 'h':
+                               print_usage("iptables-restore",
+                                           IPTABLES_VERSION);
+                               break;
+                       case 'n':
+                               noflush = 1;
+                               break;
+                       case 'M':
+                               modprobe = optarg;
+                               break;
+               }
+       }
+       
+       if (optind == argc - 1) {
+               in = fopen(argv[optind], "r");
+               if (!in) {
+                       fprintf(stderr, "Can't open %s: %s", argv[optind],
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       else if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline");
+               exit(1);
+       }
+       else in = stdin;
+       
+       /* Grab standard input. */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               int ret = 0;
+
+               line++;
+               if (buffer[0] == '\n') continue;
+               else if (buffer[0] == '#') {
+                       if (verbose) fputs(buffer, stdout);
+                       continue;
+               } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+                       DEBUGP("Calling commit\n");
+                       ret = iptc_commit(&handle);
+                       in_table = 0;
+               } else if ((buffer[0] == '*') && (!in_table)) {
+                       /* New table */
+                       char *table;
+
+                       table = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, table '%s'\n", line, table);
+                       if (!table) {
+                               exit_error(PARAMETER_PROBLEM, 
+                                       "%s: line %u table name invalid\n",
+                                       program_name, line);
+                               exit(1);
+                       }
+                       strncpy(curtable, table, IPT_TABLE_MAXNAMELEN);
+
+                       if (handle)
+                               iptc_free(&handle);
+
+                       handle = create_handle(table, modprobe);
+                       if (noflush == 0) {
+                               DEBUGP("Cleaning all chains of table '%s'\n",
+                                       table);
+                               for_each_chain(flush_entries, verbose, 1, 
+                                               &handle);
+       
+                               DEBUGP("Deleting all user-defined chains "
+                                      "of table '%s'\n", table);
+                               for_each_chain(delete_chain, verbose, 0, 
+                                               &handle) ;
+                       }
+
+                       ret = 1;
+                       in_table = 1;
+
+               } else if ((buffer[0] == ':') && (in_table)) {
+                       /* New chain. */
+                       char *policy, *chain;
+
+                       chain = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, chain '%s'\n", line, chain);
+                       if (!chain) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u chain name invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (!iptc_builtin(chain, handle)) {
+                               DEBUGP("Creating new chain '%s'\n", chain);
+                               if (!iptc_create_chain(chain, &handle)) 
+                                       exit_error(PARAMETER_PROBLEM, 
+                                                  "error creating chain "
+                                                  "'%s':%s\n", chain, 
+                                                  strerror(errno));
+                       }
+
+                       policy = strtok(NULL, " \t\n");
+                       DEBUGP("line %u, policy '%s'\n", line, policy);
+                       if (!policy) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u policy invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (strcmp(policy, "-") != 0) {
+                               struct ipt_counters count;
+
+                               if (counters) {
+                                       char *ctrs;
+                                       ctrs = strtok(NULL, " \t\n");
+
+                                       parse_counters(ctrs, &count);
+
+                               } else {
+                                       memset(&count, 0, 
+                                              sizeof(struct ipt_counters));
+                               }
+
+                               DEBUGP("Setting policy of chain %s to %s\n",
+                                       chain, policy);
+
+                               if (!iptc_set_policy(chain, policy, &count,
+                                                    &handle))
+                                       exit_error(OTHER_PROBLEM,
+                                               "Can't set policy `%s'"
+                                               " on `%s' line %u: %s\n",
+                                               chain, policy, line,
+                                               iptc_strerror(errno));
+                       }
+
+                       ret = 1;
+
+               } else if (in_table) {
+                       int a;
+                       char *ptr = buffer;
+                       char *pcnt = NULL;
+                       char *bcnt = NULL;
+                       char *parsestart;
+
+                       /* the parser */
+                       char *param_start, *curchar;
+                       int quote_open;
+
+                       /* reset the newargv */
+                       newargc = 0;
+
+                       if (buffer[0] == '[') {
+                               /* we have counters in our input */
+                               ptr = strchr(buffer, ']');
+                               if (!ptr)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               pcnt = strtok(buffer+1, ":");
+                               if (!pcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need :\n",
+                                                  line);
+
+                               bcnt = strtok(NULL, "]");
+                               if (!bcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               /* start command parsing after counter */
+                               parsestart = ptr + 1;
+                       } else {
+                               /* start command parsing at start of line */
+                               parsestart = buffer;
+                       }
+
+                       add_argv(argv[0]);
+                       add_argv("-t");
+                       add_argv((char *) &curtable);
+                       
+                       if (counters && pcnt && bcnt) {
+                               add_argv("--set-counters");
+                               add_argv((char *) pcnt);
+                               add_argv((char *) bcnt);
+                       }
+
+                       /* After fighting with strtok enough, here's now
+                        * a 'real' parser. According to Rusty I'm now no
+                        * longer a real hacker, but I can live with that */
+
+                       quote_open = 0;
+                       param_start = parsestart;
+                       
+                       for (curchar = parsestart; *curchar; curchar++) {
+                               if (*curchar == '"') {
+                                       if (quote_open) {
+                                               quote_open = 0;
+                                               *curchar = ' ';
+                                       } else {
+                                               quote_open = 1;
+                                               param_start++;
+                                       }
+                               } 
+                               if (*curchar == ' '
+                                   || *curchar == '\t'
+                                   || * curchar == '\n') {
+                                       char param_buffer[1024];
+                                       int param_len = curchar-param_start;
+
+                                       if (quote_open)
+                                               continue;
+
+                                       if (!param_len) {
+                                               /* two spaces? */
+                                               param_start++;
+                                               continue;
+                                       }
+                                       
+                                       /* end of one parameter */
+                                       strncpy(param_buffer, param_start,
+                                               param_len);
+                                       *(param_buffer+param_len) = '\0';
+
+                                       /* check if table name specified */
+                                       if (!strncmp(param_buffer, "-t", 3)
+                                            || !strncmp(param_buffer, "--table", 8)) {
+                                               exit_error(PARAMETER_PROBLEM, 
+                                                  "Line %u seems to have a "
+                                                  "-t table option.\n", line);
+                                               exit(1);
+                                       }
+
+                                       add_argv(param_buffer);
+                                       param_start += param_len + 1;
+                               } else {
+                                       /* regular character, skip */
+                               }
+                       }
+
+                       DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
+                               newargc, curtable);
+
+                       for (a = 0; a < newargc; a++)
+                               DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+                       ret = do_command(newargc, newargv, 
+                                        &newargv[2], &handle);
+
+                       free_argv();
+               }
+               if (!ret) {
+                       fprintf(stderr, "%s: line %u failed\n",
+                                       program_name, line);
+                       exit(1);
+               }
+       }
+
+       return 0;
+}
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.c.selinux b/iptables.c.selinux
new file mode 100644 (file)
index 0000000..187fd86
--- /dev/null
@@ -0,0 +1,2298 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC    0x0001
+#define FMT_NOCOUNTS   0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS    0x0008
+#define FMT_NOTABLE    0x0010
+#define FMT_NOTARGET   0x0020
+#define FMT_VIA                0x0040
+#define FMT_NONEWLINE  0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                       | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE               0x0000U
+#define CMD_INSERT             0x0001U
+#define CMD_DELETE             0x0002U
+#define CMD_DELETE_NUM         0x0004U
+#define CMD_REPLACE            0x0008U
+#define CMD_APPEND             0x0010U
+#define CMD_LIST               0x0020U
+#define CMD_FLUSH              0x0040U
+#define CMD_ZERO               0x0080U
+#define CMD_NEW_CHAIN          0x0100U
+#define CMD_DELETE_CHAIN       0x0200U
+#define CMD_SET_POLICY         0x0400U
+#define CMD_CHECK              0x0800U
+#define CMD_RENAME_CHAIN       0x1000U
+#define NUMBER_OF_CMD  13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+                                'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE       0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_SOURCE     0x00002U
+#define OPT_DESTINATION        0x00004U
+#define OPT_PROTOCOL   0x00008U
+#define OPT_JUMP       0x00010U
+#define OPT_VERBOSE    0x00020U
+#define OPT_EXPANDED   0x00040U
+#define OPT_VIANAMEIN  0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_FRAGMENT    0x00200U
+#define OPT_LINENUMBERS 0x00400U
+#define OPT_COUNTERS   0x00800U
+#define NUMBER_OF_OPT  12
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'};
+
+static struct option original_opts[] = {
+       { "append", 1, 0, 'A' },
+       { "delete", 1, 0,  'D' },
+       { "insert", 1, 0,  'I' },
+       { "replace", 1, 0,  'R' },
+       { "list", 2, 0,  'L' },
+       { "flush", 2, 0,  'F' },
+       { "zero", 2, 0,  'Z' },
+       { "new-chain", 1, 0,  'N' },
+       { "delete-chain", 2, 0,  'X' },
+       { "rename-chain", 1, 0,  'E' },
+       { "policy", 1, 0,  'P' },
+       { "source", 1, 0, 's' },
+       { "destination", 1, 0,  'd' },
+       { "src", 1, 0,  's' }, /* synonym */
+       { "dst", 1, 0,  'd' }, /* synonym */
+       { "protocol", 1, 0,  'p' },
+       { "in-interface", 1, 0, 'i' },
+       { "jump", 1, 0, 'j' },
+       { "table", 1, 0, 't' },
+       { "match", 1, 0, 'm' },
+       { "numeric", 0, 0, 'n' },
+       { "out-interface", 1, 0, 'o' },
+       { "verbose", 0, 0, 'v' },
+       { "exact", 0, 0, 'x' },
+       { "fragments", 0, 0, 'f' },
+       { "version", 0, 0, 'V' },
+       { "help", 2, 0, 'h' },
+       { "line-numbers", 0, 0, '0' },
+       { "modprobe", 1, 0, 'M' },
+       { "set-counters", 1, 0, 'c' },
+       { 0 }
+};
+
+/* we need this for iptables-restore.  iptables-restore.c sets line to the
+ * current line of the input file, in order  to give a more precise error
+ * message.  iptables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+#ifndef __OPTIMIZE__
+struct ipt_entry_target *
+ipt_get_target(struct ipt_entry *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+       /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x','x',' '},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*CHECK*/     {'x','+','+','+','x',' ','x',' ',' ',' ','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IPT_INV_SRCIP,
+/* -d */ IPT_INV_DSTIP,
+/* -p */ IPT_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IPT_INV_VIA_IN,
+/* -o */ IPT_INV_VIA_OUT,
+/* -f */ IPT_INV_FRAG,
+/*--line*/ 0
+};
+
+const char *program_version;
+const char *program_name;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct iptables_match *iptables_matches = NULL;
+struct iptables_target *iptables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries(const iptc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmp", IPPROTO_ICMP },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+       unsigned int i;
+
+       if (proto && !nolookup) {
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent)
+                       return pent->p_name;
+       }
+
+       for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+               if (chain_protos[i].num == proto)
+                       return chain_protos[i].name;
+
+       return NULL;
+}
+
+struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+       static struct in_addr addr;
+       unsigned char *addrp;
+       char *p, *q;
+       unsigned int onebyte;
+       int i;
+       char buf[20];
+
+       /* copy dotted string, because we need to modify it */
+       strncpy(buf, dotted, sizeof(buf) - 1);
+       addrp = (unsigned char *) &(addr.s_addr);
+
+       p = buf;
+       for (i = 0; i < 3; i++) {
+               if ((q = strchr(p, '.')) == NULL)
+                       return (struct in_addr *) NULL;
+
+               *q = '\0';
+               if (string_to_number(p, 0, 255, &onebyte) == -1)
+                       return (struct in_addr *) NULL;
+
+               addrp[i] = (unsigned char) onebyte;
+               p = q + 1;
+       }
+
+       /* we've checked 3 bytes, now we check the last one */
+       if (string_to_number(p, 0, 255, &onebyte) == -1)
+               return (struct in_addr *) NULL;
+
+       addrp[3] = (unsigned char) onebyte;
+
+       return &addr;
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+       struct netent *net;
+       static struct in_addr addr;
+
+       if ((net = getnetbyname(name)) != NULL) {
+               if (net->n_addrtype != AF_INET)
+                       return (struct in_addr *) NULL;
+               addr.s_addr = htonl((unsigned long) net->n_net);
+               return &addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+       /* memcpy(dst, src, sizeof(struct in_addr)); */
+       dst->s_addr = src->s_addr;
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       if (status == PARAMETER_PROBLEM)
+               exit_tryhelp(status);
+       if (status == VERSION_PROBLEM)
+               fprintf(stderr,
+                       "Perhaps iptables or your kernel needs to be upgraded.\n");
+       exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+       if (line != -1)
+               fprintf(stderr, "Error occurred at line: %d\n", line);
+       fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+                       program_name, program_name );
+       exit(status);
+}
+
+void
+exit_printhelp(void)
+{
+       struct iptables_match *m = NULL;
+       struct iptables_target *t = NULL;
+
+       printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+              program_name, program_version, program_name, program_name,
+              program_name, program_name, program_name, program_name,
+              program_name, program_name);
+
+       printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain          Append to chain\n"
+"  --delete  -D chain          Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"                              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"                              Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"                              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]                List the rules in a chain or all chains\n"
+"  --flush   -F [chain]                Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]                Zero counters in chain or all chains\n"
+"  --new     -N chain          Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]                Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"                              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"                              Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --proto     -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask]\n"
+"                              source specification\n"
+"  --destination -d [!] address[/mask]\n"
+"                              destination specification\n"
+"  --in-interface -i [!] input name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --jump      -j target\n"
+"                              target for rule (may load target extension)\n"
+"  --match     -m match\n"
+"                              extended match (may load extension)\n"
+"  --numeric   -n              numeric output of addresses and ports\n"
+"  --out-interface -o [!] output name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --table     -t table        table to manipulate (default: `filter')\n"
+"  --verbose   -v              verbose mode\n"
+"  --line-numbers              print line numbers when listing\n"
+"  --exact     -x              expand numbers (display exact values)\n"
+"[!] --fragment        -f              match second or further fragments only\n"
+"  --modprobe=<command>                try to insert modules using this command\n"
+"  --set-counters PKTS BYTES   set the counter during insert/append\n"
+"[!] --version -V              print package version.\n");
+
+       /* Print out any special helps. A user might like to be able
+          to add a --help to the commandline, and see expected
+          results. So we call help for all matches & targets */
+       for (t=iptables_targets;t;t=t->next) {
+               printf("\n");
+               t->help();
+       }
+       for (m=iptables_matches;m;m=m->next) {
+               printf("\n");
+               m->help();
+       }
+       exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 0; j < NUMBER_OF_CMD; j++) {
+                       if (!(command & (1<<j)))
+                               continue;
+
+                       if (!(options & (1<<i))) {
+                               if (commands_v_options[j][i] == '+')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "You need to supply the `-%c' "
+                                                  "option for this command\n",
+                                                  optflags[i]);
+                       } else {
+                               if (commands_v_options[j][i] != 'x')
+                                       legal = 1;
+                               else if (legal == 0)
+                                       legal = -1;
+                       }
+               }
+               if (legal == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Illegal option `-%c' with this command\n",
+                                  optflags[i]);
+       }
+}
+
+static char
+opt2char(int option)
+{
+       const char *ptr;
+       for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+       const char *ptr;
+       for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static void
+add_command(int *cmd, const int newcmd, const int othercmds, int invert)
+{
+       if (invert)
+               exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+       if (*cmd & (~othercmds))
+               exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+                          cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+       *cmd |= newcmd;
+}
+
+int
+check_inverse(const char option[], int *invert, int *optind, int argc)
+{
+       if (option && strcmp(option, "!") == 0) {
+               if (*invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Multiple `!' flags not allowed");
+               *invert = TRUE;
+               if (optind) {
+                       *optind = *optind+1;
+                       if (argc && *optind > argc)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "no argument following `!'");
+               }
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+       void *p;
+
+       if ((p = calloc(count, size)) == NULL) {
+               perror("iptables: calloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+       void *p;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("iptables: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+       struct hostent *host;
+       struct in_addr *addr;
+       unsigned int i;
+
+       *naddr = 0;
+       if ((host = gethostbyname(name)) != NULL) {
+               if (host->h_addrtype != AF_INET ||
+                   host->h_length != sizeof(struct in_addr))
+                       return (struct in_addr *) NULL;
+
+               while (host->h_addr_list[*naddr] != (char *) NULL)
+                       (*naddr)++;
+               addr = fw_calloc(*naddr, sizeof(struct in_addr));
+               for (i = 0; i < *naddr; i++)
+                       inaddrcpy(&(addr[i]),
+                                 (struct in_addr *) host->h_addr_list[i]);
+               return addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+       struct hostent *host;
+
+       if ((host = gethostbyaddr((char *) addr,
+                                 sizeof(struct in_addr), AF_INET)) != NULL)
+               return (char *) host->h_name;
+
+       return (char *) NULL;
+}
+
+/*
+ *     All functions starting with "parse" should succeed, otherwise
+ *     the program fails.
+ *     Most routines return pointers to static data that may change
+ *     between calls to the same or other routines with a few exceptions:
+ *     "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *     return global static data.
+*/
+
+static struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+       struct in_addr *addrp, *addrptmp;
+
+       if ((addrptmp = dotted_to_addr(name)) != NULL ||
+           (addrptmp = network_to_addr(name)) != NULL) {
+               addrp = fw_malloc(sizeof(struct in_addr));
+               inaddrcpy(addrp, addrptmp);
+               *naddrs = 1;
+               return addrp;
+       }
+       if ((addrp = host_to_addr(name, naddrs)) != NULL)
+               return addrp;
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+       static struct in_addr maskaddr;
+       struct in_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 32 bits */
+               maskaddr.s_addr = 0xFFFFFFFF;
+               return &maskaddr;
+       }
+       if ((addrp = dotted_to_addr(mask)) != NULL)
+               /* dotted_to_addr already returns a network byte order addr */
+               return addrp;
+       if (string_to_number(mask, 0, 32, &bits) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", mask);
+       if (bits != 0) {
+               maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+               return &maskaddr;
+       }
+
+       maskaddr.s_addr = 0L;
+       return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+                     struct in_addr *maskp, unsigned int *naddrs)
+{
+       struct in_addr *addrp;
+       char buf[256];
+       char *p;
+       int i, j, k, n;
+
+       strncpy(buf, name, sizeof(buf) - 1);
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               addrp = parse_mask(p + 1);
+       } else
+               addrp = parse_mask(NULL);
+       inaddrcpy(maskp, addrp);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (maskp->s_addr == 0L)
+               strcpy(buf, "0.0.0.0");
+
+       addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+       n = *naddrs;
+       for (i = 0, j = 0; i < n; i++) {
+               addrp[j++].s_addr &= maskp->s_addr;
+               for (k = 0; k < j - 1; k++) {
+                       if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+                               (*naddrs)--;
+                               j--;
+                               break;
+                       }
+               }
+       }
+}
+
+struct iptables_match *
+find_match(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_match *ptr;
+
+       for (ptr = iptables_matches; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified target as match. */
+                       ptr = find_match(name, DONT_LOAD);
+
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load match `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load match `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find match `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+static struct iptables_match *
+find_proto(const char *pname, enum ipt_tryload tryload, int nolookup)
+{
+       unsigned int proto;
+
+       if (string_to_number(pname, 0, 255, &proto) != -1) {
+               char *protoname = proto_to_name(proto, nolookup);
+
+               if (protoname)
+                       return find_match(protoname, tryload);
+       } else
+               return find_match(pname, tryload);
+
+       return NULL;
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+       unsigned int proto;
+
+       if (string_to_number(s, 0, 255, &proto) == -1) {
+               struct protoent *pent;
+
+               if ((pent = getprotobyname(s)))
+                       proto = pent->p_proto;
+               else {
+                       unsigned int i;
+                       for (i = 0;
+                            i < sizeof(chain_protos)/sizeof(struct pprot);
+                            i++) {
+                               if (strcmp(s, chain_protos[i].name) == 0) {
+                                       proto = chain_protos[i].num;
+                                       break;
+                               }
+                       }
+                       if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unknown protocol `%s' specified",
+                                          s);
+               }
+       }
+
+       return (u_int16_t)proto;
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if (vialen == 0)
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (!isalnum(vianame[i]) 
+                           && vianame[i] != '_' 
+                           && vianame[i] != '.') {
+                               printf("Warning: wierd character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+       unsigned int rulenum;
+
+       if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid rule number `%s'", rule);
+
+       return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name `%s' (%i chars max)",
+                          targetname, sizeof(ipt_chainlabel)-1);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+       struct netent *net;
+
+       if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+               return (char *) net->n_name;
+
+       return (char *) NULL;
+}
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+       static char buf[20];
+       const unsigned char *bytep;
+
+       bytep = (const unsigned char *) &(addrp->s_addr);
+       sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+       return buf;
+}
+
+char *
+addr_to_anyname(const struct in_addr *addr)
+{
+       char *name;
+
+       if ((name = addr_to_host(addr)) != NULL ||
+           (name = addr_to_network(addr)) != NULL)
+               return name;
+
+       return addr_to_dotted(addr);
+}
+
+char *
+mask_to_dotted(const struct in_addr *mask)
+{
+       int i;
+       static char buf[20];
+       u_int32_t maskaddr, bits;
+
+       maskaddr = ntohl(mask->s_addr);
+
+       if (maskaddr == 0xFFFFFFFFL)
+               /* we don't want to see "/32" */
+               return "";
+
+       i = 32;
+       bits = 0xFFFFFFFEL;
+       while (--i >= 0 && maskaddr != bits)
+               bits <<= 1;
+       if (i >= 0)
+               sprintf(buf, "/%d", i);
+       else
+               /* mask was not a decent combination of 1's and 0's */
+               sprintf(buf, "/%s", addr_to_dotted(mask));
+
+       return buf;
+}
+
+int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+                unsigned int *ret)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+          int invert)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+
+       if (invert) {
+               unsigned int i;
+               for (i = 0; 1 << i != option; i++);
+
+               if (!inverse_for_options[i])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot have ! before -%c",
+                                  opt2char(option));
+               *invflg |= inverse_for_options[i];
+       }
+}
+
+struct iptables_target *
+find_target(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_target *ptr;
+
+       /* Standard target? */
+       if (strcmp(name, "") == 0
+           || strcmp(name, IPTC_LABEL_ACCEPT) == 0
+           || strcmp(name, IPTC_LABEL_DROP) == 0
+           || strcmp(name, IPTC_LABEL_QUEUE) == 0
+           || strcmp(name, IPTC_LABEL_RETURN) == 0)
+               name = "standard";
+
+       for (ptr = iptables_targets; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified match as a target. */
+                       ptr = find_target(name, DONT_LOAD);
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load target `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load target `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find target `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+             unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+void
+register_match(struct iptables_match *me)
+{
+       struct iptables_match **i;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_match(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: match `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Append to list. */
+       for (i = &iptables_matches; *i; i = &(*i)->next);
+       me->next = NULL;
+       *i = me;
+
+       me->m = NULL;
+       me->mflags = 0;
+}
+
+void
+register_target(struct iptables_target *me)
+{
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_target(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: target `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Prepend to list. */
+       me->next = iptables_targets;
+       iptables_targets = me;
+       me->t = NULL;
+       me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+       if (format & FMT_KILOMEGAGIGA) {
+               if (number > 99999) {
+                       number = (number + 500) / 1000;
+                       if (number > 9999) {
+                               number = (number + 500) / 1000;
+                               if (number > 9999) {
+                                       number = (number + 500) / 1000;
+                                       if (number > 9999) {
+                                               number = (number + 500) / 1000;
+                                               printf(FMT("%4lluT ","%lluT "), number);
+                                       }
+                                       else printf(FMT("%4lluG ","%lluG "), number);
+                               }
+                               else printf(FMT("%4lluM ","%lluM "), number);
+                       } else
+                               printf(FMT("%4lluK ","%lluK "), number);
+               } else
+                       printf(FMT("%5llu ","%llu "), number);
+       } else
+               printf(FMT("%8llu ","%llu "), number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, iptc_handle_t *handle)
+{
+       struct ipt_counters counters;
+       const char *pol = iptc_get_policy(chain, &counters, handle);
+       printf("Chain %s", chain);
+       if (pol) {
+               printf(" (policy %s", pol);
+               if (!(format & FMT_NOCOUNTS)) {
+                       fputc(' ', stdout);
+                       print_num(counters.pcnt, (format|FMT_NOTABLE));
+                       fputs("packets, ", stdout);
+                       print_num(counters.bcnt, (format|FMT_NOTABLE));
+                       fputs("bytes", stdout);
+               }
+               printf(")\n");
+       } else {
+               unsigned int refs;
+               if (!iptc_get_references(&refs, chain, handle))
+                       printf(" (ERROR obtaining refs)\n");
+               else
+                       printf(" (%u references)\n", refs);
+       }
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4s ", "%s "), "num");
+       if (!(format & FMT_NOCOUNTS)) {
+               if (format & FMT_KILOMEGAGIGA) {
+                       printf(FMT("%5s ","%s "), "pkts");
+                       printf(FMT("%5s ","%s "), "bytes");
+               } else {
+                       printf(FMT("%8s ","%s "), "pkts");
+                       printf(FMT("%10s ","%s "), "bytes");
+               }
+       }
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ","%s "), "target");
+       fputs(" prot ", stdout);
+       if (format & FMT_OPTIONS)
+               fputs("opt", stdout);
+       if (format & FMT_VIA) {
+               printf(FMT(" %-6s ","%s "), "in");
+               printf(FMT("%-6s ","%s "), "out");
+       }
+       printf(FMT(" %-19s ","%s "), "source");
+       printf(FMT(" %-19s "," %s "), "destination");
+       printf("\n");
+}
+
+
+static int
+print_match(const struct ipt_entry_match *m,
+           const struct ipt_ip *ip,
+           int numeric)
+{
+       struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD);
+
+       if (match) {
+               if (match->print)
+                       match->print(ip, m, numeric);
+               else
+                       printf("%s ", match->name);
+       } else {
+               if (m->u.user.name[0])
+                       printf("UNKNOWN match `%s' ", m->u.user.name);
+       }
+       /* Don't stop iterating. */
+       return 0;
+}
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct ipt_entry *fw,
+              const char *targname,
+              unsigned int num,
+              unsigned int format,
+              const iptc_handle_t handle)
+{
+       struct iptables_target *target = NULL;
+       const struct ipt_entry_target *t;
+       u_int8_t flags;
+       char buf[BUFSIZ];
+
+       if (!iptc_is_chain(targname, handle))
+               target = find_target(targname, TRY_LOAD);
+       else
+               target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       flags = fw->ip.flags;
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4u ", "%u "), num+1);
+
+       if (!(format & FMT_NOCOUNTS)) {
+               print_num(fw->counters.pcnt, format);
+               print_num(fw->counters.bcnt, format);
+       }
+
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ", "%s "), targname);
+
+       fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
+       {
+               char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
+               if (pname)
+                       printf(FMT("%-5s", "%s "), pname);
+               else
+                       printf(FMT("%-5hu", "%hu "), fw->ip.proto);
+       }
+
+       if (format & FMT_OPTIONS) {
+               if (format & FMT_NOTABLE)
+                       fputs("opt ", stdout);
+               fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
+               fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
+               fputc(' ', stdout);
+       }
+
+       if (format & FMT_VIA) {
+               char iface[IFNAMSIZ+2];
+
+               if (fw->ip.invflags & IPT_INV_VIA_IN) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.iniface[0] != '\0') {
+                       strcat(iface, fw->ip.iniface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT(" %-6s ","in %s "), iface);
+
+               if (fw->ip.invflags & IPT_INV_VIA_OUT) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.outiface[0] != '\0') {
+                       strcat(iface, fw->ip.outiface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT("%-6s ","out %s "), iface);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
+       if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","%s "), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
+               strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
+               printf(FMT("%-19s ","%s "), buf);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
+       if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","-> %s"), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
+               strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
+               printf(FMT("%-19s ","-> %s"), buf);
+       }
+
+       if (format & FMT_NOTABLE)
+               fputs("  ", stdout);
+
+       IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
+
+       if (target) {
+               if (target->print)
+                       /* Print the target information. */
+                       target->print(&fw->ip, t, format & FMT_NUMERIC);
+       } else if (t->u.target_size != sizeof(*t))
+               printf("[%u bytes of unknown target data] ",
+                      t->u.target_size - sizeof(*t));
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ipt_entry *fw,
+                   const iptc_handle_t h)
+{
+       struct ipt_entry_target *t;
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_append_entry(chain, fw, handle);
+               }
+       }
+
+       return ret;
+}
+
+static int
+replace_entry(const ipt_chainlabel chain,
+             struct ipt_entry *fw,
+             unsigned int rulenum,
+             const struct in_addr *saddr,
+             const struct in_addr *daddr,
+             int verbose,
+             iptc_handle_t *handle)
+{
+       fw->ip.src.s_addr = saddr->s_addr;
+       fw->ip.dst.s_addr = daddr->s_addr;
+
+       if (verbose)
+               print_firewall_line(fw, *handle);
+       return iptc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int rulenum,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_insert_entry(chain, fw, rulenum, handle);
+               }
+       }
+
+       return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct ipt_entry *fw)
+{
+       /* Establish mask for comparison */
+       unsigned int size;
+       struct iptables_match *m;
+       unsigned char *mask, *mptr;
+
+       size = sizeof(struct ipt_entry);
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+       }
+
+       mask = fw_calloc(1, size
+                        + IPT_ALIGN(sizeof(struct ipt_entry_target))
+                        + iptables_targets->size);
+
+       memset(mask, 0xFF, sizeof(struct ipt_entry));
+       mptr = mask + sizeof(struct ipt_entry);
+
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memset(mptr, 0xFF,
+                      IPT_ALIGN(sizeof(struct ipt_entry_match))
+                      + m->userspacesize);
+               mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+       }
+
+       memset(mptr, 0xFF,
+              IPT_ALIGN(sizeof(struct ipt_entry_target))
+              + iptables_targets->userspacesize);
+
+       return mask;
+}
+
+static int
+delete_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+       unsigned char *mask;
+
+       mask = make_delete_mask(fw);
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_delete_entry(chain, fw, mask, handle);
+               }
+       }
+       return ret;
+}
+
+int
+for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
+              int verbose, int builtinstoo, iptc_handle_t *handle)
+{
+        int ret = 1;
+       const char *chain;
+       char *chains;
+       unsigned int i, chaincount = 0;
+
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               chaincount++;
+               chain = iptc_next_chain(handle);
+        }
+
+       chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount);
+       i = 0;
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               strcpy(chains + i*sizeof(ipt_chainlabel), chain);
+               i++;
+               chain = iptc_next_chain(handle);
+        }
+
+       for (i = 0; i < chaincount; i++) {
+               if (!builtinstoo
+                   && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
+                                   *handle))
+                       continue;
+               ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
+       }
+
+       free(chains);
+        return ret;
+}
+
+int
+flush_entries(const ipt_chainlabel chain, int verbose,
+             iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(flush_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Flushing chain `%s'\n", chain);
+       return iptc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(zero_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Zeroing chain `%s'\n", chain);
+       return iptc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(delete_chain, verbose, 0, handle);
+
+       if (verbose)
+               fprintf(stdout, "Deleting chain `%s'\n", chain);
+       return iptc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ipt_chainlabel chain, int verbose, int numeric,
+            int expanded, int linenumbers, iptc_handle_t *handle)
+{
+       int found = 0;
+       unsigned int format;
+       const char *this;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       for (this = iptc_first_chain(handle);
+            this;
+            this = iptc_next_chain(handle)) {
+               const struct ipt_entry *i;
+               unsigned int num;
+
+               if (chain && strcmp(chain, this) != 0)
+                       continue;
+
+               if (found) printf("\n");
+
+               print_header(format, this, handle);
+               i = iptc_first_rule(this, handle);
+
+               num = 0;
+               while (i) {
+                       print_firewall(i,
+                                      iptc_get_target(i, handle),
+                                      num++,
+                                      format,
+                                      *handle);
+                       i = iptc_next_rule(i, handle);
+               }
+               found = 1;
+       }
+
+       errno = ENOENT;
+       return found;
+}
+
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = malloc(1024);
+       if (ret) {
+               switch (read(procfile, ret, 1024)) {
+               case -1: goto fail;
+               case 1024: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+int iptables_insmod(const char *modname, const char *modprobe)
+{
+       char *buf = NULL;
+       char *argv[3];
+
+       /* If they don't explicitly set it, read out of kernel */
+       if (!modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+
+       switch (fork()) {
+       case 0:
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               argv[2] = NULL;
+               execv(argv[0], argv);
+
+               /* not usually reached */
+               exit(0);
+       case -1:
+               return -1;
+
+       default: /* parent */
+               wait(NULL);
+       }
+
+       free(buf);
+       return 0;
+}
+
+static struct ipt_entry *
+generate_entry(const struct ipt_entry *fw,
+              struct iptables_match *matches,
+              struct ipt_entry_target *target)
+{
+       unsigned int size;
+       struct iptables_match *m;
+       struct ipt_entry *e;
+
+       size = sizeof(struct ipt_entry);
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += m->m->u.match_size;
+       }
+
+       e = fw_malloc(size + target->u.target_size);
+       *e = *fw;
+       e->target_offset = size;
+       e->next_offset = size + target->u.target_size;
+
+       size = 0;
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memcpy(e->elems + size, m->m, m->m->u.match_size);
+               size += m->m->u.match_size;
+       }
+       memcpy(e->elems + size, target, target->u.target_size);
+
+       return e;
+}
+
+int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
+{
+       struct ipt_entry fw, *e = NULL;
+       int invert = 0;
+       unsigned int nsaddrs = 0, ndaddrs = 0;
+       struct in_addr *saddrs = NULL, *daddrs = NULL;
+
+       int c, verbose = 0;
+       const char *chain = NULL;
+       const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+       const char *policy = NULL, *newname = NULL;
+       unsigned int rulenum = 0, options = 0, command = 0;
+       const char *pcnt = NULL, *bcnt = NULL;
+       int ret = 1;
+       struct iptables_match *m;
+       struct iptables_target *target = NULL;
+       struct iptables_target *t;
+       const char *jumpto = "";
+       char *protocol = NULL;
+       const char *modprobe = NULL;
+       int proto_used = 0;
+
+       memset(&fw, 0, sizeof(fw));
+
+       opts = original_opts;
+       global_option_offset = 0;
+
+       /* re-set optind to 0 in case do_command gets called
+        * a second time */
+       optind = 0;
+
+       /* clear mflags in case do_command gets called a second time
+        * (we clear the global list of all matches for security)*/
+       for (m = iptables_matches; m; m = m->next) {
+               m->mflags = 0;
+               m->used = 0;
+       }
+
+       for (t = iptables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+          "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
+                                          opts, NULL)) != -1) {
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'A':
+                       add_command(&command, CMD_APPEND, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       add_command(&command, CMD_DELETE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!') {
+                               rulenum = parse_rulenumber(argv[optind++]);
+                               command = CMD_DELETE_NUM;
+                       }
+                       break;
+
+               case 'R':
+                       add_command(&command, CMD_REPLACE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a rule number",
+                                          cmd2char(CMD_REPLACE));
+                       break;
+
+               case 'I':
+                       add_command(&command, CMD_INSERT, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else rulenum = 1;
+                       break;
+
+               case 'L':
+                       add_command(&command, CMD_LIST, CMD_ZERO,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'F':
+                       add_command(&command, CMD_FLUSH, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'Z':
+                       add_command(&command, CMD_ZERO, CMD_LIST,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                               && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'N':
+                       if (optarg && *optarg == '-')
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name not allowed to start "
+                                          "with `-'\n");
+                       if (find_target(optarg, TRY_LOAD))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name may not clash "
+                                          "with target name\n");
+                       add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'X':
+                       add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'E':
+                       add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               newname = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires old-chain-name and "
+                                          "new-chain-name",
+                                           cmd2char(CMD_RENAME_CHAIN));
+                       break;
+
+               case 'P':
+                       add_command(&command, CMD_SET_POLICY, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               policy = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a chain and a policy",
+                                          cmd2char(CMD_SET_POLICY));
+                       break;
+
+               case 'h':
+                       if (!optarg)
+                               optarg = argv[optind];
+
+                       /* iptables -p icmp -h */
+                       if (!iptables_matches && protocol)
+                               find_match(protocol, TRY_LOAD);
+
+                       exit_printhelp();
+
+                       /*
+                        * Option selection
+                        */
+               case 'p':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
+                                  invert);
+
+                       /* Canonicalize into lower case */
+                       for (protocol = argv[optind-1]; *protocol; protocol++)
+                               *protocol = tolower(*protocol);
+
+                       protocol = argv[optind-1];
+                       fw.ip.proto = parse_protocol(protocol);
+
+                       if (fw.ip.proto == 0
+                           && (fw.ip.invflags & IPT_INV_PROTO))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "rule would never match protocol");
+                       fw.nfcache |= NFC_IP_PROTO;
+                       break;
+
+               case 's':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_SOURCE, &fw.ip.invflags,
+                                  invert);
+                       shostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP_SRC;
+                       break;
+
+               case 'd':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
+                                  invert);
+                       dhostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP_DST;
+                       break;
+
+               case 'j':
+                       set_option(&options, OPT_JUMP, &fw.ip.invflags,
+                                  invert);
+                       jumpto = parse_target(optarg);
+                       /* TRY_LOAD (may be chain name) */
+                       target = find_target(jumpto, TRY_LOAD);
+
+                       if (target) {
+                               size_t size;
+
+                               size = IPT_ALIGN(sizeof(struct ipt_entry_target))
+                                       + target->size;
+
+                               target->t = fw_calloc(1, size);
+                               target->t->u.target_size = size;
+                               strcpy(target->t->u.user.name, jumpto);
+                               target->init(target->t, &fw.nfcache);
+                               opts = merge_options(opts, target->extra_opts, &target->option_offset);
+                       }
+                       break;
+
+
+               case 'i':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.iniface,
+                                       fw.ip.iniface_mask);
+                       fw.nfcache |= NFC_IP_IF_IN;
+                       break;
+
+               case 'o':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.outiface,
+                                       fw.ip.outiface_mask);
+                       fw.nfcache |= NFC_IP_IF_OUT;
+                       break;
+
+               case 'f':
+                       set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
+                                  invert);
+                       fw.ip.flags |= IPT_F_FRAG;
+                       fw.nfcache |= NFC_IP_FRAG;
+                       break;
+
+               case 'v':
+                       if (!verbose)
+                               set_option(&options, OPT_VERBOSE,
+                                          &fw.ip.invflags, invert);
+                       verbose++;
+                       break;
+
+               case 'm': {
+                       size_t size;
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --match");
+
+                       m = find_match(optarg, LOAD_MUST_SUCCEED);
+                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                        + m->size;
+                       m->m = fw_calloc(1, size);
+                       m->m->u.match_size = size;
+                       strcpy(m->m->u.user.name, m->name);
+                       m->init(m->m, &fw.nfcache);
+                       opts = merge_options(opts, m->extra_opts, &m->option_offset);
+               }
+               break;
+
+               case 'n':
+                       set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 't':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --table");
+                       *table = argv[optind-1];
+                       break;
+
+               case 'x':
+                       set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'V':
+                       if (invert)
+                               printf("Not %s ;-)\n", program_version);
+                       else
+                               printf("%s v%s\n",
+                                      program_name, program_version);
+                       exit(0);
+
+               case '0':
+                       set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'M':
+                       modprobe = optarg;
+                       break;
+
+               case 'c':
+
+                       set_option(&options, OPT_COUNTERS, &fw.ip.invflags,
+                                  invert);
+                       pcnt = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               bcnt = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c requires packet and byte counter",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c packet counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c byte counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+                       
+                       break;
+
+
+               case 1: /* non option */
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "multiple consecutive ! not"
+                                                  " allowed");
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+
+               default:
+                       /* FIXME: This scheme doesn't allow two of the same
+                          matches --RR */
+                       if (!target
+                           || !(target->parse(c - target->option_offset,
+                                              argv, invert,
+                                              &target->tflags,
+                                              &fw, &target->t))) {
+                               for (m = iptables_matches; m; m = m->next) {
+                                       if (!m->used)
+                                               continue;
+
+                                       if (m->parse(c - m->option_offset,
+                                                    argv, invert,
+                                                    &m->mflags,
+                                                    &fw,
+                                                    &fw.nfcache,
+                                                    &m->m))
+                                               break;
+                               }
+
+                               /* If you listen carefully, you can
+                                  actually hear this code suck. */
+
+                               /* some explanations (after four different bugs
+                                * in 3 different releases): If we encountere a
+                                * parameter, that has not been parsed yet,
+                                * it's not an option of an explicitly loaded
+                                * match or a target.  However, we support
+                                * implicit loading of the protocol match
+                                * extension.  '-p tcp' means 'l4 proto 6' and
+                                * at the same time 'load tcp protocol match on
+                                * demand if we specify --dport'.
+                                *
+                                * To make this work, we need to make sure:
+                                * - the parameter has not been parsed by
+                                *   a match (m above)
+                                * - a protocol has been specified
+                                * - the protocol extension has not been
+                                *   loaded yet, or is loaded and unused
+                                *   [think of iptables-restore!]
+                                * - the protocol extension can be successively
+                                *   loaded
+                                */
+                               if (m == NULL
+                                   && protocol
+                                   && (!find_proto(protocol, DONT_LOAD,
+                                                  options&OPT_NUMERIC) 
+                                       || (find_proto(protocol, DONT_LOAD,
+                                                       options&OPT_NUMERIC)
+                                           && (proto_used == 0))
+                                      )
+                                   && (m = find_proto(protocol, TRY_LOAD,
+                                                      options&OPT_NUMERIC))) {
+                                       /* Try loading protocol */
+                                       size_t size;
+                                       
+                                       proto_used = 1;
+
+                                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                                        + m->size;
+
+                                       m->m = fw_calloc(1, size);
+                                       m->m->u.match_size = size;
+                                       strcpy(m->m->u.user.name, m->name);
+                                       m->init(m->m, &fw.nfcache);
+
+                                       opts = merge_options(opts,
+                                           m->extra_opts, &m->option_offset);
+
+                                       optind--;
+                                       continue;
+                               }
+                               if (!m)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind-1]);
+                       }
+               }
+               invert = FALSE;
+       }
+
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               m->final_check(m->mflags);
+       }
+
+       if (target)
+               target->final_check(target->tflags);
+
+       /* Fix me: must put inverse options checking here --MN */
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "nothing appropriate following !");
+
+       if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+               if (!(options & OPT_DESTINATION))
+                       dhostnetworkmask = "0.0.0.0/0";
+               if (!(options & OPT_SOURCE))
+                       shostnetworkmask = "0.0.0.0/0";
+       }
+
+       if (shostnetworkmask)
+               parse_hostnetworkmask(shostnetworkmask, &saddrs,
+                                     &(fw.ip.smsk), &nsaddrs);
+
+       if (dhostnetworkmask)
+               parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+                                     &(fw.ip.dmsk), &ndaddrs);
+
+       if ((nsaddrs > 1 || ndaddrs > 1) &&
+           (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+               exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+                          " source or destination IP addresses");
+
+       if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+               exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+                          "specify a unique address");
+
+       generic_opt_check(command, options);
+
+       if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
+               exit_error(PARAMETER_PROBLEM,
+                          "chain name `%s' too long (must be under %i chars)",
+                          chain, IPT_FUNCTION_MAXNAMELEN);
+
+       /* only allocate handle if we weren't called with a handle */
+       if (!*handle)
+               *handle = iptc_init(*table);
+
+       if (!*handle) {
+               /* try to insmod the module if iptc_init failed */
+               iptables_insmod("ip_tables", modprobe);
+               *handle = iptc_init(*table);
+       }
+
+       if (!*handle)
+               exit_error(VERSION_PROBLEM,
+                          "can't initialize iptables table `%s': %s",
+                          *table, iptc_strerror(errno));
+
+       if (command == CMD_APPEND
+           || command == CMD_DELETE
+           || command == CMD_INSERT
+           || command == CMD_REPLACE) {
+               if (strcmp(chain, "PREROUTING") == 0
+                   || strcmp(chain, "INPUT") == 0) {
+                       /* -o not valid with incoming packets. */
+                       if (options & OPT_VIANAMEOUT)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEOUT),
+                                          chain);
+               }
+
+               if (strcmp(chain, "POSTROUTING") == 0
+                   || strcmp(chain, "OUTPUT") == 0) {
+                       /* -i not valid with outgoing packets */
+                       if (options & OPT_VIANAMEIN)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEIN),
+                                          chain);
+               }
+
+               if (target && iptc_is_chain(jumpto, *handle)) {
+                       printf("Warning: using chain %s, not extension\n",
+                              jumpto);
+
+                       target = NULL;
+               }
+
+               /* If they didn't specify a target, or it's a chain
+                  name, use standard. */
+               if (!target
+                   && (strlen(jumpto) == 0
+                       || iptc_is_chain(jumpto, *handle))) {
+                       size_t size;
+
+                       target = find_target(IPT_STANDARD_TARGET,
+                                            LOAD_MUST_SUCCEED);
+
+                       size = sizeof(struct ipt_entry_target)
+                               + target->size;
+                       target->t = fw_calloc(1, size);
+                       target->t->u.target_size = size;
+                       strcpy(target->t->u.user.name, jumpto);
+                       target->init(target->t, &fw.nfcache);
+               }
+
+               if (!target) {
+                       /* it is no chain, and we can't load a plugin.
+                        * We cannot know if the plugin is corrupt, non
+                        * existant OR if the user just misspelled a
+                        * chain. */
+                       find_target(jumpto, LOAD_MUST_SUCCEED);
+               } else {
+                       e = generate_entry(&fw, iptables_matches, target->t);
+               }
+       }
+
+       switch (command) {
+       case CMD_APPEND:
+               ret = append_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE:
+               ret = delete_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE_NUM:
+               ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
+               break;
+       case CMD_REPLACE:
+               ret = replace_entry(chain, e, rulenum - 1,
+                                   saddrs, daddrs, options&OPT_VERBOSE,
+                                   handle);
+               break;
+       case CMD_INSERT:
+               ret = insert_entry(chain, e, rulenum - 1,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_LIST:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               break;
+       case CMD_FLUSH:
+               ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_ZERO:
+               ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_LIST|CMD_ZERO:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               if (ret)
+                       ret = zero_entries(chain,
+                                          options&OPT_VERBOSE, handle);
+               break;
+       case CMD_NEW_CHAIN:
+               ret = iptc_create_chain(chain, handle);
+               break;
+       case CMD_DELETE_CHAIN:
+               ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_RENAME_CHAIN:
+               ret = iptc_rename_chain(chain, newname, handle);
+               break;
+       case CMD_SET_POLICY:
+               ret = iptc_set_policy(chain, policy, NULL, handle);
+               break;
+       default:
+               /* We should never reach this... */
+               exit_tryhelp(2);
+       }
+
+       if (verbose > 1)
+               dump_entries(*handle);
+
+       return ret;
+}
diff --git a/libipq/libipq.c.netlink b/libipq/libipq.c.netlink
new file mode 100644 (file)
index 0000000..709c8a2
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * libipq.c
+ *
+ * IPQ userspace library.
+ *
+ * Please note that this library is still developmental, and there may
+ * be some API changes.
+ *
+ * Author: James Morris <jmorris@intercode.com.au>
+ *
+ * 07-11-2001 Modified by Fernando Anton to add support for IPv6.
+ *
+ * Copyright (c) 2000-2001 Netfilter Core Team
+ *
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <libipq/libipq.h>
+
+/****************************************************************************
+ *
+ * Private interface
+ *
+ ****************************************************************************/
+
+enum {
+       IPQ_ERR_NONE = 0,
+       IPQ_ERR_IMPL,
+       IPQ_ERR_HANDLE,
+       IPQ_ERR_SOCKET,
+       IPQ_ERR_BIND,
+       IPQ_ERR_BUFFER,
+       IPQ_ERR_RECV,
+       IPQ_ERR_NLEOF,
+       IPQ_ERR_ADDRLEN,
+       IPQ_ERR_STRUNC,
+       IPQ_ERR_RTRUNC,
+       IPQ_ERR_NLRECV,
+       IPQ_ERR_SEND,
+       IPQ_ERR_SUPP,
+       IPQ_ERR_RECVBUF,
+       IPQ_ERR_TIMEOUT,
+        IPQ_ERR_PROTOCOL
+};
+#define IPQ_MAXERR IPQ_ERR_PROTOCOL
+
+struct ipq_errmap_t {
+       int errcode;
+       char *message;
+} ipq_errmap[] = {
+       { IPQ_ERR_NONE, "Unknown error" },
+       { IPQ_ERR_IMPL, "Implementation error" },
+       { IPQ_ERR_HANDLE, "Unable to create netlink handle" },
+       { IPQ_ERR_SOCKET, "Unable to create netlink socket" },
+       { IPQ_ERR_BIND, "Unable to bind netlink socket" },
+       { IPQ_ERR_BUFFER, "Unable to allocate buffer" },
+       { IPQ_ERR_RECV, "Failed to receive netlink message" },
+       { IPQ_ERR_NLEOF, "Received EOF on netlink socket" },
+       { IPQ_ERR_ADDRLEN, "Invalid peer address length" },
+       { IPQ_ERR_STRUNC, "Sent message truncated" },
+       { IPQ_ERR_RTRUNC, "Received message truncated" },
+       { IPQ_ERR_NLRECV, "Received error from netlink" },
+       { IPQ_ERR_SEND, "Failed to send netlink message" },
+       { IPQ_ERR_SUPP, "Operation not supported" },
+       { IPQ_ERR_RECVBUF, "Receive buffer size invalid" },
+       { IPQ_ERR_TIMEOUT, "Timeout"},
+       { IPQ_ERR_PROTOCOL, "Invalid protocol specified" }
+};
+
+static int ipq_errno = IPQ_ERR_NONE;
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+                                  const void *msg, size_t len);
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+                                    unsigned char *buf, size_t len,
+                                    int timeout);
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+                                   const struct msghdr *msg,
+                                   unsigned int flags);
+
+static char *ipq_strerror(int errcode);
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+                                  const void *msg, size_t len)
+{
+       int status = sendto(h->fd, msg, len, 0,
+                           (struct sockaddr *)&h->peer, sizeof(h->peer));
+       if (status < 0)
+               ipq_errno = IPQ_ERR_SEND;
+       return status;
+}
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+                                   const struct msghdr *msg,
+                                   unsigned int flags)
+{
+       int status = sendmsg(h->fd, msg, flags);
+       if (status < 0)
+               ipq_errno = IPQ_ERR_SEND;
+       return status;
+}
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+                                    unsigned char *buf, size_t len,
+                                    int timeout)
+{
+       int addrlen, status;
+       struct nlmsghdr *nlh;
+
+       if (len < sizeof(struct nlmsgerr)) {
+               ipq_errno = IPQ_ERR_RECVBUF;
+               return -1;
+       }
+       addrlen = sizeof(h->peer);
+
+       if (timeout != 0) {
+               int ret;
+               struct timeval tv;
+               fd_set read_fds;
+               
+               if (timeout < 0) {
+                       /* non-block non-timeout */
+                       tv.tv_sec = 0;
+                       tv.tv_usec = 0;
+               } else {
+                       tv.tv_sec = timeout / 1000000;
+                       tv.tv_usec = timeout % 1000000;
+               }
+
+               FD_ZERO(&read_fds);
+               FD_SET(h->fd, &read_fds);
+               ret = select(h->fd+1, &read_fds, NULL, NULL, &tv);
+               if (ret < 0) {
+                       if (errno == EINTR) {
+                               return 0;
+                       } else {
+                               ipq_errno = IPQ_ERR_RECV;
+                               return -1;
+                       }
+               }
+               if (!FD_ISSET(h->fd, &read_fds)) {
+                       ipq_errno = IPQ_ERR_TIMEOUT;
+                       return 0;
+               }
+       }
+       status = recvfrom(h->fd, buf, len, 0,
+                             (struct sockaddr *)&h->peer, &addrlen);
+       if (status < 0) {
+               ipq_errno = IPQ_ERR_RECV;
+               return status;
+       }
+       if (addrlen != sizeof(h->peer)) {
+               ipq_errno = IPQ_ERR_RECV;
+               return -1;
+       }
+       if (status == 0) {
+               ipq_errno = IPQ_ERR_NLEOF;
+               return -1;
+       }
+       nlh = (struct nlmsghdr *)buf;
+       if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
+               ipq_errno = IPQ_ERR_RTRUNC;
+               return -1;
+       }
+       return status;
+}
+
+static char *ipq_strerror(int errcode)
+{
+       if (errcode < 0 || errcode > IPQ_MAXERR)
+               errcode = IPQ_ERR_IMPL;
+       return ipq_errmap[errcode].message;
+}
+
+/****************************************************************************
+ *
+ * Public interface
+ *
+ ****************************************************************************/
+
+/*
+ * Create and initialise an ipq handle.
+ */
+struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol)
+{
+       int status;
+       struct ipq_handle *h;
+
+       h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
+       if (h == NULL) {
+               ipq_errno = IPQ_ERR_HANDLE;
+               return NULL;
+       }
+       
+       memset(h, 0, sizeof(struct ipq_handle));
+       
+        if (protocol == PF_INET)
+                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
+        else if (protocol == PF_INET6)
+                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);
+        else {
+               ipq_errno = IPQ_ERR_PROTOCOL;
+               free(h);
+               return NULL;
+        }
+        
+       if (h->fd == -1) {
+               ipq_errno = IPQ_ERR_SOCKET;
+               close(h->fd);
+               free(h);
+               return NULL;
+       }
+       memset(&h->local, 0, sizeof(struct sockaddr_nl));
+       h->local.nl_family = AF_NETLINK;
+       h->local.nl_pid = getpid();
+       h->local.nl_groups = 0;
+       status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
+       if (status == -1) {
+               ipq_errno = IPQ_ERR_BIND;
+               close(h->fd);
+               free(h);
+               return NULL;
+       }
+       memset(&h->peer, 0, sizeof(struct sockaddr_nl));
+       h->peer.nl_family = AF_NETLINK;
+       h->peer.nl_pid = 0;
+       h->peer.nl_groups = 0;
+       return h;
+}
+
+/*
+ * No error condition is checked here at this stage, but it may happen
+ * if/when reliable messaging is implemented.
+ */
+int ipq_destroy_handle(struct ipq_handle *h)
+{
+       if (h) {
+               close(h->fd);
+               free(h);
+       }
+       return 0;
+}
+
+int ipq_set_mode(const struct ipq_handle *h,
+                 u_int8_t mode, size_t range)
+{
+       struct {
+               struct nlmsghdr nlh;
+               ipq_peer_msg_t pm;
+       } req;
+
+       memset(&req, 0, sizeof(req));
+       req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
+       req.nlh.nlmsg_flags = NLM_F_REQUEST;
+       req.nlh.nlmsg_type = IPQM_MODE;
+       req.nlh.nlmsg_pid = h->local.nl_pid;
+       req.pm.msg.mode.value = mode;
+       req.pm.msg.mode.range = range;
+       return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
+}
+
+/*
+ * timeout is in microseconds (1 second is 1000000 (1 million) microseconds)
+ *
+ */
+ssize_t ipq_read(const struct ipq_handle *h,
+                 unsigned char *buf, size_t len, int timeout)
+{
+       return ipq_netlink_recvfrom(h, buf, len, timeout);
+}
+
+int ipq_message_type(const unsigned char *buf)
+{
+       return ((struct nlmsghdr*)buf)->nlmsg_type;
+}
+
+int ipq_get_msgerr(const unsigned char *buf)
+{
+       struct nlmsghdr *h = (struct nlmsghdr *)buf;
+       struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+       return -err->error;
+}
+
+ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
+{
+       return NLMSG_DATA((struct nlmsghdr *)(buf));
+}
+
+int ipq_set_verdict(const struct ipq_handle *h,
+                    ipq_id_t id,
+                    unsigned int verdict,
+                    size_t data_len,
+                    unsigned char *buf)
+{
+       unsigned char nvecs;
+       size_t tlen;
+       struct nlmsghdr nlh;
+       ipq_peer_msg_t pm;
+       struct iovec iov[3];
+       struct msghdr msg;
+
+       memset(&nlh, 0, sizeof(nlh));
+       nlh.nlmsg_flags = NLM_F_REQUEST;
+       nlh.nlmsg_type = IPQM_VERDICT;
+       nlh.nlmsg_pid = h->local.nl_pid;
+       memset(&pm, 0, sizeof(pm));
+       pm.msg.verdict.value = verdict;
+       pm.msg.verdict.id = id;
+       pm.msg.verdict.data_len = data_len;
+       iov[0].iov_base = &nlh;
+       iov[0].iov_len = sizeof(nlh);
+       iov[1].iov_base = &pm;
+       iov[1].iov_len = sizeof(pm);
+       tlen = sizeof(nlh) + sizeof(pm);
+       nvecs = 2;
+       if (data_len && buf) {
+               iov[2].iov_base = buf;
+               iov[2].iov_len = data_len;
+               tlen += data_len;
+               nvecs++;
+       }
+       msg.msg_name = (void *)&h->peer;
+       msg.msg_namelen = sizeof(h->peer);
+       msg.msg_iov = iov;
+       msg.msg_iovlen = nvecs;
+       msg.msg_control = NULL;
+       msg.msg_controllen = 0;
+       msg.msg_flags = 0;
+       nlh.nlmsg_len = tlen;
+       return ipq_netlink_sendmsg(h, &msg, 0);
+}
+
+/* Not implemented yet */
+int ipq_ctl(const struct ipq_handle *h, int request, ...)
+{
+       return 1;
+}
+
+char *ipq_errstr(void)
+{
+       return ipq_strerror(ipq_errno);
+}
+
+void ipq_perror(const char *s)
+{
+       if (s)
+               fputs(s, stderr);
+       else
+               fputs("ERROR", stderr);
+       if (ipq_errno)
+               fprintf(stderr, ": %s", ipq_errstr());
+       if (errno)
+               fprintf(stderr, ": %s", strerror(errno));
+       fputc('\n', stderr);
+}
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/.#libiptc.c.1.40 b/libiptc2/.#libiptc.c.1.40
new file mode 100644 (file)
index 0000000..ce4af9b
--- /dev/null
@@ -0,0 +1,1972 @@
+/* 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>:
+ *     - 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>:
+ *     - total reimplementation
+ */
+#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 rule_head *target;       /* target of this rule, in case
+                                          it is a jump rule */
+
+
+       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_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;
+
+       struct counter_map *counter_map;
+
+       /* 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 rule_head *ruleh_get_n(struct chain_head *chain, int num)
+{
+       return NULL;
+}
+
+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_free(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);
+       }
+
+       list_del(&chainh->list);
+}
+
+#if 0
+static inline int
+get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       return 0;
+}
+#endif
+
+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;
+}
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries.entrytable;
+}
+
+#if 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
+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;
+}
+#endif
+
+/* 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->counter_map = (void *)h
+               + sizeof(STRUCT_TC_HANDLE)
+               + size;
+       strcpy(h->info.name, tablename);
+       strcpy(h->entries.name, tablename);
+       INIT_LIST_HEAD(&h->chains);
+
+       return h;
+}
+
+struct rule_head *
+append_entrycopy(STRUCT_ENTRY *e, struct chain_head *chain)
+{
+       struct rule_head *ruleh = ruleh_alloc(e->next_offset);
+       if (!ruleh)
+               return NULL;
+       
+       memcpy(&ruleh->entry, e, e->next_offset);
+       ruleh->chain = chain;
+       ruleh->entry_blob = e;
+       list_append(&ruleh->list, &chain->rules);
+
+       return ruleh;
+}
+
+
+/* have to return 0 on success, bcf ENTRY_ITERATE */
+static inline int 
+parse_entry(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;
+
+       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 */
+               append_entrycopy(e, *curchain);
+               /* FIXME: */
+       } 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) {
+                       /* FIXME: error handling */
+                       return 1;
+               }
+
+               *curchain = chainh_alloc(h, tgt->err.error);
+               /* FIXME: error handling */
+               append_entrycopy(e, *curchain);
+       } 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 */
+               append_entrycopy(e, *curchain);
+               *curchain = NULL;
+       } else {
+               /* normal rule */
+               append_entrycopy(e, *curchain);
+       }
+
+       /* 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;
+       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);
+       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.next) {
+               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
+
+/* 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 (list_empty(&handle->chains))
+               return NULL;
+
+       list_for_each(pos, &handle->chains) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (!strcmp(c->name, name))
+                       return c;
+       }
+
+       return NULL;
+}
+
+/* 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;
+       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 (%d)\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 */
+       /* FIXME: this needs to deal with internal jump targets */
+       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 chainstart {
+               STRUCT_ENTRY head;
+               struct ipt_error_target name;
+       } *newc1;
+       struct chainend {
+               STRUCT_ENTRY ret;
+               STRUCT_STANDARD_TARGET target;
+       } *newc2;
+       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;
+       }
+       
+       newc1 = ruleh_alloc(sizeof(*newc1));
+       if (!newc1) {
+               chainh_free(chead);
+               return 0;
+       }
+
+       newc2 = ruleh_alloc(sizeof(*newc2));
+       if (!newc2) {
+               chainh_free(chead);
+               ruleh_free(newc1);
+               return 0;
+       }
+
+       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);
+       newc->target.target.u.target_size
+               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       newc->target.verdict = RETURN;
+
+       list_prepend(newc1, &chead->rules);
+       list_append(newc2, &chead->rules);
+
+       return 1;
+}
+
+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/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*/