This commit was manufactured by cvs2svn to create tag iptables-1_2_9-2_3_1
authorPlanet-Lab Support <support@planet-lab.org>
Mon, 10 Jan 2005 20:29:31 +0000 (20:29 +0000)
committerPlanet-Lab Support <support@planet-lab.org>
Mon, 10 Jan 2005 20:29:31 +0000 (20:29 +0000)
'iptables-1_2_9-2_3_1'.

17 files changed:
.#iptables.c.1.54 [new file with mode: 0644]
.#iptables.c.1.55 [new file with mode: 0644]
.cvsignore [deleted file]
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/.cvsignore [deleted file]
extensions/libipt_icmp.c.print_type [new file with mode: 0644]
ip6tables.c.selinux [new file with mode: 0644]
iptables-config [deleted file]
iptables-restore.c.counters [new file with mode: 0644]
iptables.c.selinux [new file with mode: 0644]
iptables.init [deleted file]
iptables.spec [deleted file]
libipq/libipq.c.netlink [new file with mode: 0644]
libiptc/.cvsignore [deleted file]
libiptc2/.#libiptc.c.1.40 [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/.cvsignore b/.cvsignore
deleted file mode 100644 (file)
index 7bdbcc3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-.makefirst
-ip6tables
-ip6tables-restore
-ip6tables-save
-iptables
-iptables-restore
-iptables-save
-*.d
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/.cvsignore b/extensions/.cvsignore
deleted file mode 100644 (file)
index a438335..0000000
+++ /dev/null
@@ -1 +0,0 @@
-*.d
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/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/iptables-config b/iptables-config
deleted file mode 100644 (file)
index 80e37fb..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-# Load additional iptables modules (nat helpers)
-#   Default: -none-
-# Space separated list of nat helpers (e.g. 'ip_nat_ftp ip_nat_irc'), which
-# are loaded after the firewall rules are applied. Options for the helpers are
-# stored in /etc/modules.conf.
-#IPTABLES_MODULES=""
-
-# Unload modules on restart and stop
-#   Value: yes|no,  default: yes
-# This option has to be 'yes' to get to a sane state for a firewall
-# restart or stop. Only set to 'no' if there are problems unloading netfilter
-# modules.
-#IPTABLES_MODULES_UNLOAD="yes"
-
-# Save current firewall rules on stop.
-#   Value: yes|no,  default: no
-# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets stopped
-# (e.g. on system shutdown).
-#IPTABLES_SAVE_ON_STOP="no"
-
-# Save current firewall rules on restart.
-#   Value: yes|no,  default: no
-# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets
-# restarted.
-#IPTABLES_SAVE_ON_RESTART="no"
-
-# Save (and restore) rule and chain counter.
-#   Value: yes|no,  default: no
-# Save counters for rules and chains to /etc/sysconfig/iptables if
-# 'service iptables save' is called or on stop or restart if SAVE_ON_STOP or
-# SAVE_ON_RESTART is enabled.
-#IPTABLES_SAVE_COUNTER="no"
-
-# Numeric status output
-#   Value: yes|no,  default: no
-# Print IP addresses and port numbers in numeric format in the status output.
-#IPTABLES_STATUS_NUMERIC="no"
diff --git a/iptables-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.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/iptables.init b/iptables.init
deleted file mode 100755 (executable)
index 5c99246..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-#!/bin/sh
-#
-# iptables     Start iptables firewall
-#
-# chkconfig: 2345 08 92
-# description: Starts, stops and saves iptables firewall
-#
-# config: /etc/sysconfig/iptables
-# config: /etc/sysconfig/iptables-config
-
-# Source function library.
-. /etc/init.d/functions
-
-IPTABLES=iptables
-IPTABLES_DATA=/etc/sysconfig/$IPTABLES
-IPTABLES_CONFIG=/etc/sysconfig/${IPTABLES}-config
-IPV=${IPTABLES%tables} # ip for ipv4 | ip6 for ipv6
-PROC_IPTABLES_NAMES=/proc/net/${IPV}_tables_names
-VAR_SUBSYS_IPTABLES=/var/lock/subsys/$IPTABLES
-
-if [ ! -x /sbin/$IPTABLES ]; then
-    echo -n $"/sbin/$IPTABLES does not exist."; warning; echo
-    exit 0
-fi
-
-if lsmod 2>/dev/null | grep -q ipchains ; then
-    echo -n $"ipchains and $IPTABLES can not be used together."; warning; echo
-    exit 0
-fi
-
-# Old or new modutils
-/sbin/modprobe --version 2>&1 | grep -q module-init-tools \
-    && NEW_MODUTILS=1 \
-    || NEW_MODUTILS=0
-
-# Default firewall configuration:
-IPTABLES_MODULES=""
-IPTABLES_MODULES_UNLOAD="yes"
-IPTABLES_SAVE_ON_STOP="no"
-IPTABLES_SAVE_ON_RESTART="no"
-IPTABLES_SAVE_COUNTER="no"
-IPTABLES_STATUS_NUMERIC="no"
-
-# Load firewall configuration.
-[ -f "$IPTABLES_CONFIG" ] && . "$IPTABLES_CONFIG"
-
-rmmod_r() {
-    # Unload module with all referring modules.
-    # At first all referring modules will be unloaded, then the module itself.
-    local mod=$1
-    local ret=0
-    local ref=
-
-    # Get referring modules.
-    # New modutils have another output format.
-    [ $NEW_MODUTILS = 1 ] \
-       && ref=`lsmod | awk "/^${mod}/ { print \\\$4; }" | tr ',' ' '` \
-       || ref=`lsmod | grep ^${mod} | cut -d "[" -s -f 2 | cut -d "]" -s -f 1`
-
-    # recursive call for all referring modules
-    for i in $ref; do
-       rmmod_r $i
-       let ret+=$?;
-    done
-
-    # Unload module.
-    # The extra test is for 2.6: The module might have autocleaned,
-    # after all referring modules are unloaded.
-    if grep -q "^${mod}" /proc/modules ; then
-       modprobe -r $mod > /dev/null 2>&1
-       let ret+=$?;
-    fi
-
-    return $ret
-}
-
-flush_n_delete() {
-    # Flush firewall rules and delete chains.
-    [ -e "$PROC_IPTABLES_NAMES" ] || return 1
-
-    # Check if firewall is configured (has tables)
-    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
-    [ -z "$tables" ] && return 1
-
-    echo -n $"Flushing firewall rules: "
-    ret=0
-    # For all tables
-    for i in $tables; do
-        # Flush firewall rules.
-       $IPTABLES -t $i -F;
-       let ret+=$?;
-
-        # Delete firewall chains.
-       $IPTABLES -t $i -X;
-       let ret+=$?;
-
-       # Set counter to zero.
-       $IPTABLES -t $i -Z;
-       let ret+=$?;
-    done
-
-    [ $ret -eq 0 ] && success || failure
-    echo
-    return $ret
-}
-
-set_policy() {
-    # Set policy for configured tables.
-    policy=$1
-
-    # Check if iptable module is loaded
-    [ ! -e "$PROC_IPTABLES_NAMES" ] && return 1
-
-    # Check if firewall is configured (has tables)
-    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
-    [ -z "$tables" ] && return 1
-
-    echo -n $"Setting chains to policy $policy: "
-    ret=0
-    for i in $tables; do
-       echo -n "$i "
-       case "$i" in
-           filter)
-                $IPTABLES -t filter -P INPUT $policy \
-                   && $IPTABLES -t filter -P OUTPUT $policy \
-                   && $IPTABLES -t filter -P FORWARD $policy \
-                   || let ret+=1
-               ;;
-           nat)
-               $IPTABLES -t nat -P PREROUTING $policy \
-                   && $IPTABLES -t nat -P POSTROUTING $policy \
-                   && $IPTABLES -t nat -P OUTPUT $policy \
-                   || let ret+=1
-               ;;
-           mangle)
-               $IPTABLES -t mangle -P PREROUTING $policy \
-                   && $IPTABLES -t mangle -P POSTROUTING $policy \
-                   && $IPTABLES -t mangle -P INPUT $policy \
-                   && $IPTABLES -t mangle -P OUTPUT $policy \
-                   && $IPTABLES -t mangle -P FORWARD $policy \
-                   || let ret+=1
-               ;;
-           *)
-               let ret+=1
-               ;;
-        esac
-    done
-
-    [ $ret -eq 0 ] && success || failure
-    echo
-    return $ret
-}
-
-start() {
-    # Do not start if there is no config file.
-    [ -f "$IPTABLES_DATA" ] || return 1
-
-    echo -n $"Applying $IPTABLES firewall rules: "
-
-    OPT=
-    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
-
-    $IPTABLES-restore $OPT $IPTABLES_DATA
-    if [ $? -eq 0 ]; then
-       success; echo
-    else
-       failure; echo; return 1
-    fi
-    
-    # Load additional modules (helpers)
-    if [ -n "$IPTABLES_MODULES" ]; then
-       echo -n $"Loading additional $IPTABLES modules: "
-       ret=0
-       for mod in $IPTABLES_MODULES; do
-           echo -n "$mod "
-           modprobe $mod > /dev/null 2>&1
-           let ret+=$?;
-       done
-       [ $ret -eq 0 ] && success || failure
-       echo
-    fi
-    
-    touch $VAR_SUBSYS_IPTABLES
-    return $ret
-}
-
-stop() {
-    # Do not stop if iptables module is not loaded.
-    [ -e "$PROC_IPTABLES_NAMES" ] || return 1
-
-    flush_n_delete
-    set_policy ACCEPT
-    
-    if [ "x$IPTABLES_MODULES_UNLOAD" = "xyes" ]; then
-       echo -n $"Unloading $IPTABLES modules: "
-       ret=0
-       rmmod_r ${IPV}_tables
-       let ret+=$?;
-       rmmod_r ${IPV}_conntrack
-       let ret+=$?;
-       [ $ret -eq 0 ] && success || failure
-       echo
-    fi
-    
-    rm -f $VAR_SUBSYS_IPTABLES
-    return $ret
-}
-
-save() {
-    # Check if iptable module is loaded
-    [ ! -e "$PROC_IPTABLES_NAMES" ] && return 1
-
-    # Check if firewall is configured (has tables)
-    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
-    [ -z "$tables" ] && return 1
-
-    echo -n $"Saving firewall rules to $IPTABLES_DATA: "
-
-    OPT=
-    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
-
-    ret=0
-    TMP_FILE=`/bin/mktemp -q /tmp/$IPTABLES.XXXXXX` \
-       && chmod 600 "$TMP_FILE" \
-       && $IPTABLES-save $OPT > $TMP_FILE 2>/dev/null \
-       && size=`stat -c '%s' $TMP_FILE` && [ $size -gt 0 ] \
-       || ret=1
-    if [ $ret -eq 0 ]; then
-       if [ -e $IPTABLES_DATA ]; then
-           cp -f $IPTABLES_DATA $IPTABLES_DATA.save \
-               && chmod 600 $IPTABLES_DATA.save \
-               || ret=1
-       fi
-       if [ $ret -eq 0 ]; then
-           cp -f $TMP_FILE $IPTABLES_DATA \
-               && chmod 600 $IPTABLES_DATA \
-               || ret=1
-       fi
-    fi
-    [ $ret -eq 0 ] && success || failure
-    echo
-    rm -f $TMP_FILE
-    return $ret
-}
-
-status() {
-    # Do not print status if lockfile is missing and iptables modules are not 
-    # loaded.
-    # Check if iptable module is loaded
-    if [ ! -f "$VAR_SUBSYS_IPTABLES" ]; then
-       echo $"Firewall is stopped."
-       return 1
-    fi
-
-    # Check if firewall is configured (has tables)
-    if [ ! -e "$PROC_IPTABLES_NAMES" ]; then
-       echo $"Firewall is not configured. "
-       return 1
-    fi
-    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
-    if [ -z "$tables" ]; then
-       echo $"Firewall is not configured. "
-       return 1
-    fi
-
-    NUM=
-    [ "x$IPTABLES_STATUS_NUMERIC" = "xyes" ] && NUM="-n"
-
-    for table in $tables; do
-       echo $"Table: $table"
-       $IPTABLES -t $table --list $NUM && echo
-    done
-
-    return 0
-}
-
-restart() {
-    [ "x$IPTABLES_SAVE_ON_RESTART" = "xyes" ] && save
-    stop
-    start
-}
-
-case "$1" in
-    start)
-       stop
-       start
-       RETVAL=$?
-       ;;
-    stop)
-       [ "x$IPTABLES_SAVE_ON_STOP" = "xyes" ] && save
-       stop
-       RETVAL=$?
-       ;;
-    restart)
-       restart
-       RETVAL=$?
-       ;;
-    condrestart)
-       [ -e "$VAR_SUBSYS_IPTABLES" ] && restart
-       ;;
-    status)
-       status
-       RETVAL=$?
-       ;;
-    panic)
-       flush_n_delete
-       set_policy DROP
-       RETVAL=$?
-        ;;
-    save)
-       save
-       RETVAL=$?
-       ;;
-    *)
-       echo $"Usage: $0 {start|stop|restart|condrestart|status|panic|save}"
-       exit 1
-       ;;
-esac
-
-exit $RETVAL
diff --git a/iptables.spec b/iptables.spec
deleted file mode 100644 (file)
index a6ddcef..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-%define name iptables
-%define version 1.2.9
-%define release 2.3.1.1.planetlab%{?date:.%{date}}
-
-Vendor: PlanetLab
-Packager: PlanetLab Central <support@planet-lab.org>
-Distribution: PlanetLab 3.0
-URL: http://cvs.planet-lab.org/cvs/iptables
-
-%define build_devel 1
-%define linux_header 0
-
-Name: %{name}
-Summary: Tools for managing Linux kernel packet filtering capabilities.
-Version: %{version}
-Release: %{release}
-Source: http://www.netfilter.org/%{name}-%{version}.tar.bz2
-%define SOURCE1 iptables.init
-%define SOURCE2 iptables-config
-%if %{linux_header}
-Source3: netfilter-2.4.20.tar.gz
-%endif
-Patch2: iptables-1.2.8-nolibnsl.patch
-Patch3: iptables-1.2.8-print_type.patch
-Patch4: iptables-1.2.9-netlink.patch
-Patch5: iptables-1.2.9-selinux.patch
-Patch6: iptables-1.2.9-counters.patch
-Group: System Environment/Base
-#URL: http://www.netfilter.org/
-BuildRoot: %{_tmppath}/%{name}-buildroot
-License: GPL
-BuildPrereq: /usr/bin/perl
-Requires: kernel >= 2.4.20
-Requires(post,postun): chkconfig
-Prefix: %{_prefix}
-
-%package ipv6
-Summary: IPv6 support for iptables.
-Group: System Environment/Base
-Requires: %{name} = %{version}
-
-%if %{build_devel}
-%package devel
-Summary: Development package for iptables.
-Group: System Environment/Base
-Requires: %{name} = %{version}
-%endif
-
-%description
-The iptables utility controls the network packet filtering code in the
-Linux kernel. If you need to set up firewalls and/or IP masquerading,
-you should install this package.
-
-%description ipv6
-The iptables package contains IPv6 (the next version of the IP
-protocol) support for iptables. Iptables controls the Linux kernel
-network packet filtering code, allowing you to set up firewalls and IP
-masquerading. 
-
-Install iptables-ipv6 if you need to set up firewalling for your
-network and you are using ipv6.
-
-%if %{build_devel}
-%description devel
-The iptables utility controls the network packet filtering code in the
-Linux kernel. If you need to set up firewalls and/or IP masquerading,
-you should install this package.
-%endif
-
-%prep
-rm -rf %{buildroot}
-
-%setup -q
-
-# Put it to a reasonable place
-find . -type f -exec perl -pi -e "s,/usr,%{prefix},g" {} \;
-
-%build
-TOPDIR=`pwd`
-OPT="$RPM_OPT_FLAGS -I$TOPDIR/include"
-# bootstrap to avoid BuildRequires of kernel-source
-for KERNEL_DIR in $RPM_BUILD_DIR/linux-* /lib/modules/`uname -r`/build /usr ; do
-    if [ -f $KERNEL_DIR/include/linux/version.h ] ; then
-       break
-    fi
-done
-make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib}
-make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib} iptables-save iptables-restore
-make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib} ip6tables-save ip6tables-restore
-
-%install
-# bootstrap to avoid BuildRequires of kernel-source
-for KERNEL_DIR in $RPM_BUILD_DIR/linux-* /lib/modules/`uname -r`/build /usr ; do
-    if [ -f $KERNEL_DIR/include/linux/version.h ] ; then
-       break
-    fi
-done
-make install DESTDIR=%{buildroot} KERNEL_DIR=/usr BINDIR=/sbin LIBDIR=/%{_lib} MANDIR=%{_mandir}
-%if %{build_devel}
-make install-devel DESTDIR=%{buildroot} KERNEL_DIR=/usr BINDIR=/sbin LIBDIR=%{_libdir} MANDIR=%{_mandir}
-%endif
-cp ip{6,}tables-{save,restore} $RPM_BUILD_ROOT/sbin
-cp iptables-*.8 $RPM_BUILD_ROOT%{_mandir}/man8
-mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
-install -c -m755 %{SOURCE1} $RPM_BUILD_ROOT/etc/rc.d/init.d/iptables
-sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE1} > ip6tables.init
-install -c -m755 ip6tables.init $RPM_BUILD_ROOT/etc/rc.d/init.d/ip6tables
-mkdir -p $RPM_BUILD_ROOT/etc/sysconfig
-install -c -m755 %{SOURCE2} $RPM_BUILD_ROOT/etc/sysconfig/iptables-config
-sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE2} > ip6tables-config
-install -c -m755 ip6tables-config $RPM_BUILD_ROOT/etc/sysconfig/ip6tables-config
-
-%clean
-rm -rf $RPM_BUILD_ROOT 
-
-%post
-/sbin/chkconfig --add iptables
-
-%preun
-if [ "$1" = 0 ]; then
-       /sbin/chkconfig --del iptables
-fi
-
-%post ipv6
-/sbin/chkconfig --add ip6tables
-
-%preun ipv6
-if [ "$1" = 0 ]; then
-       /sbin/chkconfig --del ip6tables
-fi
-
-%files
-%defattr(-,root,root,0755)
-%doc COPYING INSTALL INCOMPATIBILITIES
-%config %attr(0755,root,root) /etc/rc.d/init.d/iptables
-%config(noreplace) %attr(0600,root,root) /etc/sysconfig/iptables-config
-/sbin/iptables*
-%{_mandir}/man8/iptables*
-%dir /%{_lib}/iptables
-/%{_lib}/iptables/libipt*
-
-%files ipv6
-%defattr(-,root,root,0755)
-%config %attr(0755,root,root) /etc/rc.d/init.d/ip6tables
-%config(noreplace) %attr(0600,root,root) /etc/sysconfig/ip6tables-config
-/sbin/ip6tables*
-%{_mandir}/man8/ip6tables*
-/%{_lib}/iptables/libip6t*
-
-%if %{build_devel}
-%files devel
-%defattr(-,root,root,0755)
-%{_includedir}/libipq.h
-%{_libdir}/libipq.a
-%{_libdir}/libiptc.a
-%{_mandir}/man3/*
-%endif
-
-%changelog
-* Tue Mar 02 2004 Elliot Lee <sopwith@redhat.com>
-- rebuilt
-
-* Thu Feb 26 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-2.3
-- fixed iptables-restore -c fault if there are no counters (#116421)
-
-* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
-- rebuilt
-
-* Sun Jan  25 2004 Dan Walsh <dwalsh@redhat.com> 1.2.9-1.2
-- Close File descriptors to prevent SELinux error message
-
-* Wed Jan  7 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-1.1
-- rebuild
-
-* Wed Dec 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.9-1
-- vew version 1.2.9
-- new config options in ipXtables-config:
-  IPTABLES_MODULES_UNLOAD
-- more documentation in ipXtables-config
-- fix for netlink security issue in libipq (devel package)
-- print fix for libipt_icmp (#109546)
-
-* Thu Oct 23 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-13
-- marked all messages in iptables init script for translation (#107462)
-- enabled devel package (#105884, #106101)
-- bumped build for fedora for libipt_recent.so (#106002)
-
-* Tue Sep 23 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-12.1
-- fixed lost udp port range in ip6tables-save (#104484)
-- fixed non numeric multiport port output in ipXtables-savs
-
-* Mon Sep 22 2003 Florian La Roche <Florian.LaRoche@redhat.de> 1.2.8-11
-- do not link against -lnsl
-
-* Wed Sep 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-10
-- made variables in rmmod_r local
-
-* Tue Jul 22 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-9
-- fixed permission for init script
-
-* Sat Jul 19 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-8
-- fixed save when iptables file is missing and iptables-config permissions
-
-* Tue Jul  8 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-7
-- fixes for ip6tables: module unloading, setting policy only for existing 
-  tables
-
-* Thu Jul  3 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-6
-- IPTABLES_SAVE_COUNTER defaults to no, now
-- install config file in /etc/sysconfig
-- exchange unload of ip_tables and ip_conntrack
-- fixed start function
-
-* Wed Jul  2 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-5
-- new config option IPTABLES_SAVE_ON_RESTART
-- init script: new status, save and restart
-- fixes #44905, #65389, #80785, #82860, #91040, #91560 and #91374
-
-* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-4
-- new config option IPTABLES_STATUS_NUMERIC
-- cleared IPTABLES_MODULES in iptables-config
-
-* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-3
-- new init scripts
-
-* Sat Jun 28 2003 Florian La Roche <Florian.LaRoche@redhat.de>
-- remove check for very old kernel versions in init scripts
-- sync up both init scripts and remove some further ugly things
-- add some docu into rpm
-
-* Thu Jun 26  2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-2
-- rebuild
-
-* Mon Jun 16 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-1
-- update to 1.2.8
-
-* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
-- rebuilt
-
-* Mon Jan 13 2003 Bill Nottingham <notting@redhat.com> 1.2.7a-1
-- update to 1.2.7a
-- add a plethora of bugfixes courtesy Michael Schwendt <mschewndt@yahoo.com>
-
-* Fri Dec 13 2002 Elliot Lee <sopwith@redhat.com> 1.2.6a-3
-- Fix multilib
-
-* Wed Aug 07 2002 Karsten Hopp <karsten@redhat.de>
-- fixed iptables and ip6tables initscript output, based on #70511
-- check return status of all iptables calls, not just the last one
-  in a 'for' loop.
-
-* Mon Jul 29 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.6a-1
-- 1.2.6a (bugfix release, #69747)
-
-* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
-- automated rebuild
-
-* Thu May 23 2002 Tim Powers <timp@redhat.com>
-- automated rebuild
-
-* Mon Mar  4 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-3
-- Add some fixes from CVS, fixing bug #60465
-
-* Tue Feb 12 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-2
-- Merge ip6tables improvements from Ian Prowell <iprowell@prowell.org>
-  #59402
-- Update URL (#59354)
-- Use /sbin/chkconfig rather than chkconfig in %postun script
-
-* Fri Jan 11 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-1
-- 1.2.5
-
-* Wed Jan 09 2002 Tim Powers <timp@redhat.com>
-- automated rebuild
-
-* Mon Nov  5 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-2
-- Fix %preun script
-
-* Tue Oct 30 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-1
-- Update to 1.2.4 (various fixes, including security fixes; among others:
-  #42990, #50500, #53325, #54280)
-- Fix init script (#31133)
-
-* Mon Sep  3 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.3-1
-- 1.2.3 (5 security fixes, some other fixes)
-- Fix updating (#53032)
-
-* Mon Aug 27 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-4
-- Fix #50990
-- Add some fixes from current CVS; should fix #52620
-
-* Mon Jul 16 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-3
-- Add some fixes from the current CVS tree; fixes #49154 and some IPv6
-  issues
-
-* Tue Jun 26 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-2
-- Fix iptables-save reject-with (#45632), Patch from Michael Schwendt
-  <mschwendt@yahoo.com>
-
-* Tue May  8 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-1
-- 1.2.2
-
-* Wed Mar 21 2001 Bernhard Rosenkraenzer <bero@redhat.com>
-- 1.2.1a, fixes #28412, #31136, #31460, #31133
-
-* Thu Mar  1 2001 Bernhard Rosenkraenzer <bero@redhat.com>
-- Yet another initscript fix (#30173)
-- Fix the fixes; they fixed some issues but broke more important
-  stuff :/ (#30176)
-
-* Tue Feb 27 2001 Bernhard Rosenkraenzer <bero@redhat.com>
-- Fix up initscript (#27962)
-- Add fixes from CVS to iptables-{restore,save}, fixing #28412
-
-* Fri Feb 09 2001 Karsten Hopp <karsten@redhat.de>
-- create /etc/sysconfig/iptables mode 600 (same problem as #24245)
-
-* Mon Feb 05 2001 Karsten Hopp <karsten@redhat.de>
-- fix bugzilla #25986 (initscript not marked as config file)
-- fix bugzilla #25962 (iptables-restore)
-- mv chkconfig --del from postun to preun
-
-* Thu Feb  1 2001 Trond Eivind Glomsrød <teg@redhat.com>
-- Fix check for ipchains
-
-* Mon Jan 29 2001 Bernhard Rosenkraenzer <bero@redhat.com>
-- Some fixes to init scripts
-
-* Wed Jan 24 2001 Bernhard Rosenkraenzer <bero@redhat.com>
-- Add some fixes from CVS, fixes among other things Bug #24732
-
-* Wed Jan 17 2001 Bernhard Rosenkraenzer <bero@redhat.com>
-- Add missing man pages, fix up init script (Bug #17676)
-
-* Mon Jan 15 2001 Bill Nottingham <notting@redhat.com>
-- add init script
-
-* Mon Jan 15 2001 Bernhard Rosenkraenzer <bero@redhat.com>
-- 1.2
-- fix up ipv6 split
-- add init script
-- Move the plugins from /usr/lib/iptables to /lib/iptables.
-  This needs to work before /usr is mounted...
-- Use -O1 on alpha (compiler bug)
-
-* Sat Jan  6 2001 Bernhard Rosenkraenzer <bero@redhat.com>
-- 1.1.2
-- Add IPv6 support (in separate package)
-
-* Thu Aug 17 2000 Bill Nottingham <notting@redhat.com>
-- build everywhere
-
-* Tue Jul 25 2000 Bernhard Rosenkraenzer <bero@redhat.com>
-- 1.1.1
-
-* Thu Jul 13 2000 Prospector <bugzilla@redhat.com>
-- automatic rebuild
-
-* Tue Jun 27 2000 Preston Brown <pbrown@redhat.com>
-- move iptables to /sbin.
-- excludearch alpha for now, not building there because of compiler bug(?)
-
-* Fri Jun  9 2000 Bill Nottingham <notting@redhat.com>
-- don't obsolete ipchains either
-- update to 1.1.0
-
-* Mon Jun  4 2000 Bill Nottingham <notting@redhat.com>
-- remove explicit kernel requirement
-
-* Tue May  2 2000 Bernhard Rosenkränzer <bero@redhat.com>
-- initial package
diff --git a/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/.cvsignore b/libiptc/.cvsignore
deleted file mode 100644 (file)
index a438335..0000000
+++ /dev/null
@@ -1 +0,0 @@
-*.d
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);
+}