]> granicus.if.org Git - ipset/commitdiff
Userspace part of sets: ipset added (JK)
authorkadlec <kadlec>
Mon, 9 Feb 2004 13:47:01 +0000 (13:47 +0000)
committerkadlec <kadlec>
Mon, 9 Feb 2004 13:47:01 +0000 (13:47 +0000)
12 files changed:
ChangeLog [new file with mode: 0644]
ChangeLog.ippool [new file with mode: 0644]
Makefile [new file with mode: 0644]
ipset [new file with mode: 0755]
ipset.8 [new file with mode: 0644]
ipset.c [new file with mode: 0644]
ipset.h [new file with mode: 0644]
ipset_iphash.c [new file with mode: 0644]
ipset_ipmap.c [new file with mode: 0644]
ipset_macipmap.c [new file with mode: 0644]
ipset_portmap.c [new file with mode: 0644]
libipt_set.h [new file with mode: 0644]

diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..c6e20fa
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,6 @@
+1.0
+ - Renamed to ipset                                      (Jozsef Kadlecsik)
+ - Rewritten to support child pools                      (Jozsef Kadlecsik)
+ - portmap, iphash pool support added                    (Jozsef Kadlecsik)
+ - too much other mods here and there to list...         (Jozsef Kadlecsik)
+
diff --git a/ChangeLog.ippool b/ChangeLog.ippool
new file mode 100644 (file)
index 0000000..669c304
--- /dev/null
@@ -0,0 +1,59 @@
+Original changelog as ippool:
+
+0.3.2b
+- Fixed missing kfree(pool)                              (Martin Josefsson)
+
+0.3.2a
+- Added libipt_pool.c and libipt_POOL.c                          (Martin Josefsson)
+
+
+0.3.2
+- Passes pointers to skb's around instead of ip's in the  (Martin Josefsson)
+  kernel modules.
+- Added a new pooltype, macipmap, which matches ip's      (Martin Josefsson)
+  against macaddresses.
+- Cleaned up a lot of typedefs.                           (Martin Josefsson)
+- Fixed an unlocking of the wrong lock.                   (Martin Josefsson)
+- Fixed a refcount bug when allocated memory was too      (Martin Josefsson)
+  small.
+- Fixed a free() of unallocated memory.                   (Martin Josefsson)
+- Switched from kmalloc/kfree to vmalloc/vfree for        (Martin Josefsson)
+  pool-listings/additions.
+
+
+0.3.1
+- Changed the API between userspace modules and base.     (Joakim Axelsson)
+  Moved the memberdata pointer to module self.
+  As a result of this Protocolversion is increased to 4.
+- Fixed problems with crashing null-pooltype              (Joakim Axelsson)
+- Fixed problems with compiling warnings                  (Joakim Axelsson)
+  in null pooltype.
+
+       
+0.3.0:
+- Changed the listing to use getsockopt.                   (Joakim Axelsson)
+  /proc is left for debuging purpose.
+  This is a mayor change. 
+  Protocolversion is increased to 3 
+- Added support for --quiet                                (Joakim Axelsson)
+- Added support for --sorted                               (Joakim Axelsson)
+- Added support for --numeric                              (Joakim Axelsson)
+- Added support for --exact                                (Joakim Axelsson)
+- Added -Z (Zero) which zero's the counter                 (Joakim Axelsson)
+  on one or all pools.
+- Added support for --debug that prints all debug-messages (Joakim Axelsosn)
+  in userspace. Need to be compiled with 
+  IP_POOL_DEBUG tho.
+- Added null pooltype. For demostration and                (Joakim Axelsson)
+  pooltype skeleton mostly                                 
+- Fixed bug with possibly renaming to an already           (Joakim Axelsson) 
+  existing pool.
+- Change error to OTHER_PROBLEM on add and del IP.         (Joakim Axelsson)
+
+0.2.1-0.2.3
+- Better handling of references                            (Patrick Schaaf)
+- Various bugfixes                                         (Patrick Schaaf)
+- Cleaning up the code in kernelspace                      (Patrick Schaaf)
+
+0.2.0:
+- Rewrote the entrie system. Modulized it.                 (Joakim Axelsson)
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..23302b0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,48 @@
+#!/usr/bin/make
+
+IPSET_VERSION:=v1.0
+IPSET_LIB_DIR:=$(DESTDIR)$(LIBDIR)/ipset
+#IPSET_LIB_DIR:=.
+#CFLAGS:=-I$(KERNEL_DIR)/include
+
+SETTYPES:=ipmap portmap macipmap iphash
+
+EXTRAS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_set.h ] && echo ipset/ipset)
+EXTRAS+=$(foreach T, $(SETTYPES),ipset/libipset_$(T).so)
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ipset $(DESTDIR)$(MANDIR)/man8/ipset.8
+EXTRA_INSTALLS+=$(foreach T, $(SETTYPES), $(DESTDIR)$(LIBDIR)/ipset/libipset_$(T).so)
+
+ifndef TOPLEVEL_INCLUDED
+local:
+       cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS)
+
+else
+EXTRA_DEPENDS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_set.h ] && echo "")
+CFLAGS+=-DIPSET_VERSION=$(IPSET_VERSION) -DIPSET_LIB_DIR=\"$(IPSET_LIB_DIR)\"
+
+#The ipset(8) self
+ipset/ipset.o: ipset/ipset.c
+       $(CC) $(CFLAGS) -g -c -o $@ $<
+
+ipset/ipset: ipset/ipset.o
+       $(CC) $(CFLAGS) -ldl -rdynamic -o $@ $^
+
+#Pooltypes
+ipset/ipset_%.o: ipset/ipset_%.c
+       $(CC) $(CFLAGS) -c -o $@ $<
+
+ipset/libipset_%.so: ipset/ipset_%.o
+       $(LD) -shared -o $@ $<
+
+$(DESTDIR)$(LIBDIR)/ipset/libipset_%.so: ipset/libipset_%.so
+       @[ -d $(DESTDIR)$(LIBDIR)/ipset ] || mkdir -p $(DESTDIR)$(LIBDIR)/ipset
+       cp $< $@
+
+$(DESTDIR)$(BINDIR)/ipset: ipset/ipset
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+$(DESTDIR)$(MANDIR)/man8/ipset.8: ipset/ipset.8
+       @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
+       cp $< $@
+endif
diff --git a/ipset b/ipset
new file mode 100755 (executable)
index 0000000..30a373c
Binary files /dev/null and b/ipset differ
diff --git a/ipset.8 b/ipset.8
new file mode 100644 (file)
index 0000000..e7aea08
--- /dev/null
+++ b/ipset.8
@@ -0,0 +1,289 @@
+.TH IPSET 8 "Feb 05, 2004" "" ""
+.\"
+.\" Man page written by 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.
+.\"
+.\"
+.SH NAME
+ipset \- IP set administration
+.SH SYNOPSIS
+.BR "ipset -N " "set type-specification [options]"
+.br
+.BR "ipset -[XFLSHh] " "[set] [options]"
+.br
+.BR "ipset -[EW] " "from-set to-set"
+.br
+.BR "ipset -[ADT] " "set entry"
+.br
+.BR "ipset -R "
+.SH DESCRIPTION
+.B Ipset
+is used to set up, maintain, and inspect so called IP sets in the Linux
+kernel. Each set can contain a fixed number of entries and at every entry
+there might be child sets, up to a given level. Child sets at the same
+level must have the same type.
+.P
+In spite it is called IP sets, depending on the type the sets may
+store not only IP addresses but (TCP/UDP) port numbers as well, or 
+additional informations besides IP addresses: the word IP means a general
+term here. See the set type definitions below.
+.P
+Child sets are identified
+by `IP', without the possible additional informations stored in the set.
+There is no other relationship between the set entry and the child set
+at that entry: the set entry can be added, deleted without disturbing
+the child set and vice versa. Therefore below we denote pure IPs
+identifying a child set by `IP', while entries in a set by `entry'.
+.SH OPTIONS
+The options that are recognized by
+.B ipset
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform.  Only one of them
+can be specified on the command line unless otherwise specified
+below.  For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B ipset
+can differentiate it from all other options.
+.TP
+.BI "-N, --create " "\fIsetname\fP type0[,type1...] type0-options"
+Create a set identified with setname, with listed types. 
+Type0-specific options must be supplied. When more than one set 
+type is given on the command line, room for the first level child 
+sets are automatically reserved.
+.TP
+.BI "-N, --create " "\fIsetname:IP[,...]\fP type type-options"
+Create a child set at setname:IP[,...] identified with type,
+which must correspond to the set type used when the set was created.
+The parent set and parent child sets must exist. When the
+.B "-c, --childsets"
+option is used, space for child sets will be reserved.
+.TP
+.BI "-X, --destroy " "[\fIsetname[:IP,...]\fP]"
+Destroy the specified set or child set, or all sets if none is
+given.
+.TP
+.BI "-F, --flush " "[\fIsetname[:IP,...]\fP]"
+Delete all entries from the specified set or child set, or from
+all sets if none is given. When the
+.B "-c, --childsets"
+option is specified, child sets are recursively flushed too.
+.TP
+.BI "-E, --rename " "\fIfrom-setname\fP \fIto-setname\fP"
+Rename a set. Set identified by to-setname must not exist.
+.TP
+.BI "-W, --swap " "\fIfrom-setname\fP \fIto-setname\fP"
+Swap the content of two sets. Both sets must exist.
+.TP
+.BI "-L, --list " "[\fIsetname[:IP,...]\fP]"
+List the entries from the specified set or child set, or from
+all sets if none is given. When the
+.B "-c, --childsets"
+option is specified, child sets are recursively listed too.
+The
+.B "-n, --numeric"
+option can be used to suppress name lookups and generate numeric
+output. When the
+.B "-s, --sorted"
+option is given, the entries are listed sorted.
+.TP
+.BI "-S, --save " "[\fIsetname[:IP,...]\fP]"
+Save the set or child set, or all sets if none specified to stdout
+in a format, that --restore can read.
+.TP
+.BI "-R, --restore "
+Restore from stdin a saved session generated by --save.
+.TP
+.BI "-A, --add " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP"
+Add an entry to a (child) set or multiple entries to the child sets
+defined by the entries. In another words, the command
+.nf
+ ipset -A set entry1,entry2
+.fi
+is equivalent to
+.nf
+ ipset -A set entry1
+ ipset -A set:entry1-IP entry2
+.fi
+.TP
+.BI "-D, --del " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP"
+Delete an entry from a (child) set or multiple entries from the child sets
+defined by the entries.
+.P
+When multiple entries are added or deleted and the command fails at
+a deeper level, the successfully added/deleted entries are not restored
+to their previous value.
+.TP
+.BI "-T, --test " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP"
+Test if an entry or multiple entries exist in a (child) set. Exit status
+number is nonzero if any tested entry is missing from the set and
+zero if they are all exists.
+.TP
+.BI "-H, --help " "[settype] [options]"
+Print help and settype specific help. Some set types has additional
+help options, see below.
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-s, --sorted"
+Sorted output. When listing sets, entries are listed sorted.
+.TP
+.B "-n, --numeric"
+Numeric output. When listing sets, IP addresses and port numbers
+will be printed in numeric format. By default the program will
+try to display them as host names, network names, or services
+(whenever applicable).
+.TP
+.B "-q, --quiet"
+Suppress any output to stdout and stderr. Ipset will still return
+possible errors.
+.TP
+.B "-c, --childsets"
+Space will be reserved for child sets when creating a set, or list/flush
+operation valid for child sets too.
+.TP
+.B "-i, --hint"
+Hint best settype initialization parameters (for hash type sets).
+.SH SET TYPES
+ipset supports the following set types:
+.SS ipmap
+The ipmap set type uses a memory range, where each bit represents
+one IP address. An ipmap set can store up to 65535 (B-class network)
+IP addresses. The ipmap set type is very fast and memory cheap, great
+for use when one want to match certain IPs in a range. Using the
+.B "--netmask"
+option with a CIDR netmask value between 0-32 when creating an ipmap
+set, you will be able to store and match network addresses: i.e an
+IP address will be in the set if the value resulted by masking the address
+with the specified netmask can be found in the set.
+.P
+Options to use when creating an ipmap set:
+.TP
+.BR "--from " from-IP
+.TP
+.BR "--to " to-IP
+Create an ipmap set from the specified range.
+.TP
+.BR "--network " IP/mask
+Create an ipmap set from the specified network.
+.TP
+.BR "--netmask " CIDR-netmask
+When the optional
+.B "--netmask"
+parameter specified, network addresses will be 
+stored in the set instead of IP addresses, and the from-IP parameter
+must be a network address.
+.SS macipmap
+The macipmap set type uses a memory range, where each 8 bytes
+represents one IP and a MAC addresses. A macipmap set type can store
+up to 65535 (B-class network) IP addresses with MAC.
+When adding an entry to a macipmap set, you must specify the entry as
+.I IP%MAC
+When deleting or testing macipmap entries, the
+.I %MAC
+part is not mandatory.
+.P
+Options to use when creating an macipmap set:
+.TP
+.BR "--from " from-IP
+.TP
+.BR "--to " to-IP
+Create a macipmap set from the specified range.
+.TP
+.BR "--network " IP/mask
+Create a macipmap set from the specified network.
+.TP
+.BR "--matchunset"
+When the optional
+.B "--matchunset"
+parameter specified, IP addresses which could be stored 
+in the set but not set yet, will always match.
+.SS portmap
+The portmap set type uses a memory range, where each bit represents
+one port. A portmap set type can store up to 65535 ports.
+The portmap set type is very fast and memory cheap.
+.P
+Options to use when creating an portmap set:
+.TP
+.BR "--from " from-port
+.TP
+.BR "--to " to-port
+Create a portmap set from the specified range.
+.SS iphash
+The iphash set type uses a fixed size hash to store the IP addresses
+and can store up to 65535 (size of a B-class network) addresses. The 
+iphash set type is fast and great for use to store
+random addresses. By supplyig the
+.B "--netmask"
+option with a CIDR netmask value between 0-32 at creating the set,
+you will be able to store and match network addresses instead: i.e 
+an IP address will be in the set if the value of the address
+masked with the specified netmask can be found in the set.
+When adding and IP address to the hash, the IP address can be preceded 
+by `+', by which you can force to overwrite already existing entries 
+in the hash.
+.P
+In help mode you can use the
+.B "--hint"
+option to find the the smallest hashsize with the corresponding initval
+for your hash entries.
+.P
+Options to use when creating an iphash set:
+.TP
+.BR "--hashsize " hashsize
+.TP
+.BR "--initval " hash-initval
+Create an iphash set with the specified hashsize and initial 
+(random) hash parameter. (The
+.B "--initval"
+parameter is optional.)
+.TP
+.BR "--netmask " CIDR-netmask
+When the optional
+.B "--netmask"
+parameter specified, network addresses will be 
+stored in the set instead of IP addresses.
+.TP
+The possible help mode options are:
+.TP
+.BI "-i, --hint"
+Enable hinting mode to find the best (smallest) hash size. The entries
+are fed via standard input.
+.TP
+.BI "--try " number
+How many times should the program try the same hash size with
+different random initvals (default 8).
+.TP
+.BI "--factor " number
+The starting hash size is so many times the number of the entries
+(default 4).
+.SH DIAGNOSTICS
+Various error messages are printed to standard error.  The exit code
+is 0 for correct functioning.  Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs? No, just funny features. :-)
+OK, just kidding...
+.SH SEE ALSO
+.BR iptables (8),
+.SH AUTHORS
+Jozsef Kadlecsik wrote ipset, which is based on ippool by
+Joakim Axelsson, Patrick Schaaf and Martin Josefsson.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
diff --git a/ipset.c b/ipset.c
new file mode 100644 (file)
index 0000000..04d2487
--- /dev/null
+++ b/ipset.c
@@ -0,0 +1,2326 @@
+/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ *                     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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <dlfcn.h>
+#include <asm/bitops.h>
+
+#include "ipset.h"
+
+/* The list of all sets */
+static struct set *all_sets = NULL;
+
+/* The list of loaded set types */
+static struct settype *all_settypes = NULL;
+
+/* Suppress output to stdout and stderr? */
+static int option_quiet = 0;
+
+/* Data from the command line: */
+static char *set_name = NULL;                  /* Name of the set */
+static char *set_typename[IP_SET_LEVELS];      /* Set typenames */
+static unsigned int set_typename_level = 0;    /* Size of set_typename */
+static ip_set_ip_t set_ip[IP_SET_LEVELS];      /* IP addresses of child set */
+static unsigned int set_level = 0;             /* Size of set_ip */
+static unsigned int ip_level = 0;              /* Size of add/del/test addresses */
+
+/* Max size of a set when hinting. */
+#define MAX_HINT_SIZE  65536
+
+#ifdef IP_SET_DEBUG
+int option_debug = 0;
+#endif
+
+#define OPTION_OFFSET 256
+static unsigned int global_option_offset = 0;
+
+/* Now most of these command parsing functions are borrowed from iptables.c */
+
+/* Commands */
+#define CMD_NONE       0x0000U
+#define CMD_CREATE     0x0001U         /* -N */
+#define CMD_DESTROY    0x0002U         /* -X */
+#define CMD_FLUSH      0x0004U         /* -F */
+#define CMD_RENAME     0x0008U         /* -E */
+#define CMD_SWAP       0x0010U         /* -W */
+#define CMD_LIST       0x0020U         /* -L */
+#define CMD_SAVE       0x0040U         /* -S */
+#define CMD_RESTORE    0x0080U         /* -R */
+#define CMD_ADD                0x0100U         /* -A */
+#define CMD_DEL                0x0200U         /* -D */
+#define CMD_TEST       0x0400U         /* -T */
+#define CMD_HELP       0x0800U         /* -H */
+#define CMD_VERSION    0x1000U         /* -V */
+#define CMD_CREATE_CHILD 0x2000U       /* -N !!! */
+#define NUMBER_OF_CMD 13
+static const char cmdflags[] = { 
+       'N', 'X', 'F', 'E', 'W', 'L', 'S', 'R', 
+       'A', 'D', 'T', 'H', 'V',
+};
+
+/* Options */
+#define OPT_NONE       0x0000U
+#define OPT_NUMERIC    0x0001U         /* -n */
+#define OPT_SORTED     0x0002U         /* -s */
+#define OPT_QUIET      0x0004U         /* -q */
+#define OPT_DEBUG      0x0008U         /* -z */
+#define OPT_CHILDSETS  0x0010U         /* -c */
+#define OPT_HINT       0x0020U         /* -i */
+#define NUMBER_OF_OPT 6
+static const char optflags[] =
+    { 'n', 's', 'q', 'z', 'c', 'i' };
+
+static struct option opts_long[] = {
+       /* set operations */
+       {"create",  1, 0, 'N'},
+       {"destroy", 2, 0, 'X'},
+       {"flush",   2, 0, 'F'},
+       {"rename",  1, 0, 'E'},
+       {"swap",    1, 0, 'W'},
+       {"list",    2, 0, 'L'},
+
+       {"save",    2, 0, 'S'},
+       {"restore", 0, 0, 'R'},
+
+       /* ip in set operations */
+       {"add",     1, 0, 'A'},
+       {"del",     1, 0, 'D'},
+       {"test",    1, 0, 'T'},
+       
+       /* free options */
+       {"numeric", 0, 0, 'n'},
+       {"sorted",  0, 0, 's'},
+       {"quiet",   0, 0, 'q'},
+       {"childsets",0, 0, 'c'},
+       {"hint",    0, 0, 'i'},
+
+#ifdef IP_SET_DEBUG
+       /* debug (if compiled with it) */
+       {"debug",   0, 0, 'z'},
+#endif
+
+       /* version and help */
+       {"version", 0, 0, 'V'},
+       {"help",    2, 0, 'H'},
+
+       /* end */
+       {0}
+};
+
+static char opts_short[] =
+    "-N:X::F::E:W:L::S::RA:D:T:nsqzciVh::H::";
+
+/* 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] = {
+       /*            -n   -s   -q   -z   -c   -i*/
+        /*CREATE*/  {'x', 'x', ' ', ' ', ' ', 'x'},
+        /*DESTROY*/ {'x', 'x', ' ', ' ', 'x', 'x'},
+        /*FLUSH*/   {'x', 'x', ' ', ' ', ' ', 'x'},
+        /*RENAME*/  {'x', 'x', ' ', ' ', 'x', 'x'},
+        /*SWAP*/    {'x', 'x', ' ', ' ', 'x', 'x'},
+        /*LIST*/    {' ', ' ', 'x', ' ', ' ', 'x'},
+        /*SAVE*/    {'x', 'x', ' ', ' ', 'x', 'x'},
+        /*RESTORE*/ {'x', 'x', ' ', ' ', 'x', 'x'},
+        /*ADD*/     {'x', 'x', ' ', ' ', 'x', 'x'},
+        /*DEL*/     {'x', 'x', ' ', ' ', 'x', 'x'},
+        /*TEST*/    {'x', 'x', ' ', ' ', 'x', 'x'},
+        /*HELP*/    {'x', 'x', 'x', ' ', 'x', ' '},
+        /*VERSION*/ {'x', 'x', 'x', ' ', 'x', 'x'},
+};
+
+void exit_tryhelp(int status)
+{
+       fprintf(stderr,
+               "Try `%s -H' or '%s --help' for more information.\n",
+               program_name, program_name);
+       exit(status);
+}
+
+void exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       if (!option_quiet) {
+               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 %s or your kernel needs to be upgraded.\n",
+                               program_name);
+       }
+
+       exit(status);
+}
+
+void ipset_printf(char *msg, ...)
+{
+       va_list args;
+
+       if (!option_quiet) {
+               va_start(args, msg);
+               vfprintf(stdout, msg, args);
+               va_end(args);
+               fprintf(stdout, "\n");
+       }
+}
+
+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 set_command(int *cmd, const int newcmd)
+{
+       if (*cmd != CMD_NONE)
+               exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+                          cmd2char(newcmd), cmd2char(newcmd));
+       *cmd = newcmd;
+}
+
+static void add_option(unsigned int *options, unsigned int option)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+}
+
+void *ipset_malloc(size_t size)
+{
+       void *p;
+
+       if (size == 0)
+               return NULL;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("ipset: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+void ipset_free(void **data)
+{
+       if (*data == NULL)
+               return;
+
+       free(*data);
+       *data = NULL;
+}
+
+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;
+}
+
+static char *ip_tohost(const struct in_addr *addr)
+{
+       struct hostent *host;
+
+       DP("ip_tohost()");
+
+       if ((host = gethostbyaddr((char *) addr,
+                                 sizeof(struct in_addr),
+                                 AF_INET)) != NULL) {
+               DP("%s", host->h_name);
+               return (char *) host->h_name;
+       }
+
+       return (char *) NULL;
+}
+
+static char *ip_tonetwork(const struct in_addr *addr)
+{
+       struct netent *net;
+
+       DP("ip_tonetwork()");
+
+       if ((net = getnetbyaddr((long) ntohl(addr->s_addr), 
+                               AF_INET)) != NULL)
+               return (char *) net->n_name;
+
+       return (char *) NULL;
+}
+
+/* Return a string representation of an IP address.
+ * Please notice that a pointer to static char* area is returned.
+ */
+char *ip_tostring(ip_set_ip_t ip, unsigned options)
+{
+       struct in_addr addr;
+       char *name;
+
+       addr.s_addr = htonl(ip);
+
+       if (!(options & OPT_NUMERIC)) {
+               if ((name = ip_tohost(&addr)) != NULL ||
+                   (name = ip_tonetwork(&addr)) != NULL)
+                       return name;
+       }
+
+       return inet_ntoa(addr);
+}
+
+/* Fills the 'ip' with the parsed ip or host in host byte order */
+void parse_ip(const char *str, ip_set_ip_t * ip)
+{
+       struct hostent *host;
+       struct in_addr addr;
+
+       if (inet_aton(str, &addr) != 0) {
+               *ip = ntohl(addr.s_addr);       /* We want host byte order */
+
+#ifdef IP_SET_DEBUG
+               {
+                       /*DEBUG*/ char *p = (char *) ip;
+                       DP("PARSE_IP %x %x %x %x %x %x", *ip, 0xC10BF8A6,
+                          p[0], p[1], p[2], p[3]);
+               }
+#endif
+
+               return;
+       }
+
+       host = gethostbyname(str);
+       if (host != NULL) {
+               if (host->h_addrtype != AF_INET ||
+                   host->h_length != sizeof(struct in_addr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "host/network `%s' not an internet name",
+                                  str);
+               if (host->h_addr_list[1] != 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "host/network `%s' resolves to serveral ip-addresses. "
+                                  "Please specify one.", str);
+
+               *ip = ntohl(((struct in_addr *) host->h_addr_list[0])->s_addr);
+
+#ifdef IP_SET_DEBUG
+               {
+                       /*DEBUG*/ char *p = (char *) ip;
+                       DP("PARSE_IP %x %x %x %x %x %x", *ip, 0xC10BF8A6,
+                          p[0], p[1], p[2], p[3]);
+               }
+#endif
+
+               return;
+       }
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", str);
+}
+
+/* Fills 'mask' with the parsed mask in host byte order */
+void parse_mask(const char *str, ip_set_ip_t * mask)
+{
+       struct in_addr addr;
+       unsigned int bits;
+
+       DP("parse_mask %s", str);
+
+       if (str == NULL) {
+               /* no mask at all defaults to 32 bits */
+               *mask = 0xFFFFFFFF;
+               return;
+       }
+       if (strchr(str, '.') && inet_aton(str, &addr) != 0) {
+               *mask = ntohl(addr.s_addr);     /* We want host byte order */
+               return;
+       }
+       if (sscanf(str, "%d", &bits) != 1 || bits < 0 || bits > 32)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", str);
+
+       DP("bits: %d", bits);
+
+       *mask = 0xFFFFFFFF << (32 - bits);
+}
+
+/* Combines parse_ip and parse_mask */
+void
+parse_ipandmask(const char *str, ip_set_ip_t * ip, ip_set_ip_t * mask)
+{
+       char buf[256];
+       char *p;
+
+       strncpy(buf, str, sizeof(buf) - 1);
+       buf[255] = '\0';
+
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               parse_mask(p + 1, mask);
+       } else
+               parse_mask(NULL, mask);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (*mask == 0U)
+               *ip = 0U;
+       else
+               parse_ip(buf, ip);
+
+       DP("parse_ipandmask: %s ip: %08X (%s) mask: %08X",
+          str, *ip, ip_tostring(*ip, 0), *mask);
+
+       /* Apply the netmask */
+       *ip &= *mask;
+
+       DP("parse_ipandmask: %s ip: %08X (%s) mask: %08X",
+          str, *ip, ip_tostring(*ip, 0), *mask);
+}
+
+/* Return a string representation of a port
+ * Please notice that a pointer to static char* area is returned
+ * and we assume TCP protocol.
+ */
+char *port_tostring(ip_set_ip_t port, unsigned options)
+{
+       struct servent *service;
+       static char name[] = "65535";
+       
+       if (!(options & OPT_NUMERIC)
+           && (service = getservbyport(htons(port), "tcp")))
+               return service->s_name;
+       
+       sprintf(name, "%u", port);
+       return name;
+}
+
+int
+string_to_number(const char *str, unsigned int min, unsigned int max,
+                ip_set_ip_t *port)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(str, &end, 0);
+       if (*end == '\0' && end != str) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *port = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static int
+string_to_port(const char *str, ip_set_ip_t *port)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(str, "tcp")) != NULL) {
+               *port = ntohs((unsigned short) service->s_port);
+               return 0;
+       }
+       
+       return -1;
+}
+
+/* Fills the 'ip' with the parsed port in host byte order */
+void parse_port(const char *str, ip_set_ip_t *port)
+{      
+       if ((string_to_number(str, 0, 65535, port) != 0)
+           && (string_to_port(str, port) != 0))
+               exit_error(PARAMETER_PROBLEM, "Invalid TCP port `%s' specified", str);
+}
+
+static struct settype *settype_find(const char *typename)
+{
+       struct settype *runner = all_settypes;
+
+       DP("settype %s", typename);
+
+       while (runner != NULL) {
+               if (strncmp(runner->typename, typename, 
+                           IP_SET_MAXNAMELEN) == 0)
+                       return runner;
+
+               runner = runner->next;
+       }
+
+       return NULL;            /* not found */
+}
+
+static struct settype *settype_load(const char *typename)
+{
+       char path[sizeof(IPSET_LIB_DIR) + sizeof(IPSET_LIB_NAME) +
+                 strlen(typename)];
+       struct settype *settype;
+
+       /* do some search in list */
+       settype = settype_find(typename);
+       if (settype != NULL)
+               return settype; /* found */
+
+       /* Else we have to load it */
+       sprintf(path, IPSET_LIB_DIR IPSET_LIB_NAME, typename);
+
+       if (dlopen(path, RTLD_NOW)) {
+               /* Found library. */
+
+               settype = settype_find(typename);
+
+               if (settype != NULL)
+                       return settype;
+       }
+
+       /* Can't load the settype */
+       exit_error(PARAMETER_PROBLEM,
+                  "Couldn't load settype `%s':%s\n",
+                  typename, dlerror());
+
+       return NULL;            /* Never executed, but keep compilers happy */
+}
+
+/* Register a new set type */
+void settype_register(struct settype *settype)
+{
+       struct settype *chk;
+
+       DP("register_settype '%s'\n", settype->typename);
+
+       /* Check if this typename already exists */
+       chk = settype_find(settype->typename);
+
+       if (chk != NULL)
+               exit_error(OTHER_PROBLEM,
+                          "Set type '%s' already registred!\n",
+                          settype->typename);
+
+       /* Check version */
+       if (settype->protocol_version != IP_SET_PROTOCOL_VERSION)
+               exit_error(OTHER_PROBLEM,
+                          "Set type is of wrong protocol version %u!"
+                          " I'm am of version %u.\n", settype->typename,
+                          settype->protocol_version,
+                          IP_SET_PROTOCOL_VERSION);
+
+       /* Insert first */
+       settype->next = all_settypes;
+       settype->data = ipset_malloc(settype->create_size);
+       all_settypes = settype;
+
+       DP("ip_set: register settype end '%s'\n", settype->typename);
+}
+
+static int kernel_getsocket(void)
+{
+       int sockfd = -1;
+
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               exit_error(OTHER_PROBLEM,
+                          "You need to be root to perform this command.");
+
+       return sockfd;
+}
+
+static void kernel_error(int err)
+{
+       /* translate errnos, as returned by our *sockopt() functions */
+       exit_error(OTHER_PROBLEM, "Error from kernel: %s", strerror(err));
+}
+
+static void kernel_sendto(void *data, size_t size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+
+       DP("kernel_sendto() res=%d errno=%d\n", res, errno);
+
+       if (res != 0)
+               kernel_error(errno);
+}
+
+/* Used by addip and delip that has to handle the EEXIST error better */
+static int kernel_sendto_handleexist(void *data, size_t size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+
+       DP("kernel_sendto_handleexist() res=%d errno=%d\n", res, errno);
+
+       if (res != 0 && errno == EEXIST)
+               return -1;
+       else if (res != 0)
+               kernel_error(errno);
+
+       return 0;               /* all ok */
+}
+
+static void kernel_getfrom(void *data, size_t * size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+
+       DP("kernel_getfrom() res=%d errno=%d\n", res, errno);
+
+       if (res != 0)
+               kernel_error(errno);
+}
+
+static int get_protocolversion(void)
+{
+       struct ip_set_req_version req_version;
+       size_t size = sizeof(struct ip_set_req_version);
+
+       req_version.op = IP_SET_OP_VERSION;
+
+       kernel_getfrom(&req_version, &size);
+
+       DP("get_protocolversion() ver=%d", req_version.version);
+
+       return req_version.version;
+}
+
+static void set_append(struct set *set)
+{
+       struct set *entry = all_sets;
+       
+       while (entry != NULL && entry->next != NULL)
+               entry = entry->next;
+       
+       if (entry == NULL)
+               all_sets = set;
+       else
+               entry->next = set;
+}
+
+static void get_sets(void)
+{
+       void *data = NULL;
+       struct ip_set_req_listing_size req_list;
+       int sockfd = kernel_getsocket();
+       size_t size;
+       size_t eaten = 0;
+       int i, res;
+
+       DP("get_sets()");
+       for (i = 0; i < LIST_TRIES; i++) {
+               req_list.op = IP_SET_OP_LISTING_SIZE;
+               size = sizeof(req_list);
+               kernel_getfrom(&req_list, &size);
+               size = req_list.size;
+
+               DP("got size: %d", size);
+
+               if (req_list.size == 0)
+                       return;         /* No sets in kernel */
+
+               data = ipset_malloc(size);
+               ((struct ip_set_req_base *) data)->op = IP_SET_OP_LISTING;
+
+               /* Get! */
+               res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, &size);
+               DP("list_get getsockopt() res=%d errno=%d\n", res, errno);
+
+               if (res == 0)
+                       goto got_sets;  /* all OK */
+               else if (errno != ENOMEM)
+                       break;          /* Not a memory error */
+
+               DP("not enough mem, looping again");
+               free(data);
+       }
+
+       if (errno == ENOMEM)
+               exit_error(OTHER_PROBLEM,
+                          "Tried to list sets from kernel %d times"
+                          " and failed. Please try again when the load on"
+                          " the sets has gone down.", LIST_TRIES);
+       else
+               kernel_error(errno);
+
+    got_sets:
+
+       DP("get_sets() - size=%d data=%p", size, data);
+       /* Handle the data */
+       while (eaten < size) {
+               struct ip_set_req_listing *header =
+                   (struct ip_set_req_listing *) (data + eaten);
+               struct set *set = ipset_malloc(sizeof(struct set));
+
+               memset(set, 0, sizeof(struct set));
+               
+               DP("fillin %d %p", eaten, header);
+
+#ifdef IP_SET_DEBUG
+               /* DEBUG */
+               {
+                       void *i;
+
+                       DP("SET DATA:");
+
+                       for (i = data + eaten;
+                            i <
+                            data + eaten +
+                            sizeof(struct ip_set_req_listing); i += 4) {
+                               unsigned *j = (unsigned *) i;
+                               DP("%x", *j);
+                       }
+               }
+#endif
+
+               /* Fill in data */
+               DP("Processing set '%s'", header->name);
+               strcpy(set->name, header->name);
+               set->id = header->id;
+               set->levels = header->levels;
+               set->ref = header->ref;
+               for (i = 0; i < set->levels; i++) {
+                       set->settype[i] = settype_load(header->typename[i]);
+                       set->private[i].adt = 
+                               ipset_malloc(set->settype[i]->req_size);
+               }
+
+               eaten += sizeof(struct ip_set_req_listing);
+
+               DP("insert in list");
+               set_append(set);
+       }
+
+       DP("free");
+
+       free(data);
+
+       DP("get_sets() eaten = %d, size = %d", eaten, size);
+
+       if (eaten != size)
+               exit_error(OTHER_PROBLEM,
+                          "Desynched in listing of sets from kernel. "
+                          "Read %d bytes. Worked %d bytes.", size, eaten);
+}
+
+struct set *set_find(const char *setname)
+{
+       struct set *set = all_sets;
+
+       DP("%s", setname);
+
+       while (set != NULL) {
+               if (strcmp(setname, set->name) == 0)
+                       return set;
+               set = set->next;
+       }
+       return NULL;
+}
+
+static struct set *set_checkname(const char *setname)
+{
+       char *saved = strdup(setname);
+       char *ptr, *tmp = saved;
+       struct set *set;
+       int i;
+
+       DP("%s", setname);
+
+       /* Cleanup */
+       if (set_name != NULL)
+               free(set_name);
+       
+       for (i = 0; i < IP_SET_SETIP_LEVELS; i++)
+               set_ip[i] = 0;
+       set_level = 0;
+
+       /* name[:ip,...] */
+       ptr = strsep(&tmp, ":");
+       if (strlen(ptr) > IP_SET_MAXNAMELEN - 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Setname '%s' in '%s' too long. Max %d characters.",
+                          ptr, setname, IP_SET_MAXNAMELEN - 1);
+
+       DP("%s (%s)", ptr, tmp);
+       set = set_find(ptr);
+       if (!set && tmp)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set '%s' not found for '%s'\n",
+                          ptr, setname);
+       
+       set_name = strdup(ptr);
+
+       while (set_level < IP_SET_SETIP_LEVELS && tmp) {
+               ptr = strsep(&tmp, ",");
+               switch (set->settype[set_level]->typecode) {
+               case IPSET_TYPE_IP:
+                       parse_ip(ptr, &set_ip[set_level++]);
+                       break;
+               case IPSET_TYPE_PORT:
+                       parse_port(ptr, &set_ip[set_level++]);
+                       break;
+               default:
+                       ; /* Keep compilers happy */
+               }               
+               if (set->levels <= set_level)
+                       exit_error(PARAMETER_PROBLEM, 
+                                  "Subset definition is too deep for set '%s'\n",
+                                  set_name);
+       }
+
+       free(saved);
+       return set;
+}
+
+static inline struct set *set_find_byname(const char *setname)
+{
+       struct set *set = set_checkname(setname);
+
+       if (!set)
+               exit_error(PARAMETER_PROBLEM, 
+                          "Set '%s' not found\n", setname);
+       return set;
+}
+
+static void set_checktype(const char *typename)
+{
+       char *saved = strdup(typename);
+       char *ptr, *tmp = saved;
+       int i;
+
+       /* Cleanup */
+       for (i = 0; i < IP_SET_LEVELS; i++)
+               if (set_typename[i] != NULL)
+                       free(set_typename[i]);
+
+       /* typename[,...] */
+       set_typename_level = 0;
+       while (set_typename_level < IP_SET_LEVELS && tmp) {
+               ptr = strsep(&tmp, ",");
+               DP("settype '%s', level %i", ptr, set_typename_level);
+               if (strlen(ptr) > IP_SET_MAXNAMELEN - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Typename '%s' in '%s' too long. Max %d characters.",
+                                  ptr, typename, IP_SET_MAXNAMELEN - 1);
+               set_typename[set_typename_level++] = strdup(ptr);
+       }
+       DP("tmp '%s', level %i", tmp, set_typename_level);
+       if (set_typename_level >= IP_SET_LEVELS || tmp) 
+               exit_error(PARAMETER_PROBLEM,
+                          "More than %d settypes in '%s'.",
+                          IP_SET_LEVELS - 1 , typename);
+       free(saved);
+}
+
+/* Get setid from kernel. */
+static void
+set_get_setid(struct set *set)
+{
+       struct ip_set_req_get req_get;
+       size_t size = sizeof(struct ip_set_req_get);
+
+       DP("set_get_setid()");
+
+       req_get.op = IP_SET_OP_GETSET_BYNAME;
+       strcpy(req_get.name, set->name);
+
+       /* Result in the id-field */
+       kernel_getfrom(&req_get, &size);
+
+       if (size != sizeof(struct ip_set_req_get))
+               exit_error(OTHER_PROBLEM,
+                          "Incorrect return size from kernel."
+                          "Should be %d but was %d.",
+                          sizeof(struct ip_set_req_get), size);
+
+       DP("set_get_setid() result=%u", req_get.id);
+
+       if (req_get.id < 0)
+               exit_error(OTHER_PROBLEM,
+                          "Set %s cannot be found.",
+                          set->name);
+
+       set->id = req_get.id;
+       set->ref = req_get.ref;
+}
+
+/* Send create set order to kernel */
+static void
+set_create(struct set *set)
+{
+       struct ip_set_req_create req_create;
+       struct settype *settype = set->settype[0];
+       size_t size;
+       void *data;
+       int i;
+
+       DP("set_create()");
+
+       req_create.op = IP_SET_OP_CREATE;
+       strcpy(req_create.name, set->name);
+       for (i = 0; i < set_typename_level; i++)
+               strcpy(req_create.typename[i], set->settype[i]->typename);
+       req_create.levels = set_typename_level;
+
+       /* Final checks */
+       settype->create_final(settype->data, settype->flags);
+
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_create) + settype->create_size;
+       data = ipset_malloc(size);
+
+       /* Add up ip_set_req_create and the settype data */
+       memcpy(data, &req_create, sizeof(struct ip_set_req_create));
+       memcpy(data + sizeof(struct ip_set_req_create),
+              settype->data, settype->create_size);
+
+#ifdef IP_SET_DEBUG
+       /* DEBUG */
+       {
+               void *i;
+
+               DP("CMD_CREATE");
+               DP("OP %u", req_create.op);
+               DP("Name: %s", req_create.name);
+               DP("Typename: %s", req_create.typename[0]);
+               DP("All data");
+
+               for (i = data; i < data + size; i += 4) {
+                       unsigned *j = (unsigned *) i;
+                       DP("%x", *j);
+               }
+       }
+#endif
+
+       kernel_sendto(data, size);
+       free(data);
+       
+       /* Get set id from kernel */
+       set_get_setid(set);
+       /* Success! */
+       set_append(set);
+}
+
+/* Send create childset order to kernel */
+static void
+set_create_childset(const struct set *set, unsigned options)
+{
+       struct ip_set_req_sub req_sub;
+       struct settype *settype = set->settype[set_level];
+       size_t size;
+       void *data;
+       int i;
+
+       DP("childset_create()");
+
+       req_sub.op = IP_SET_OP_CREATE_CHILD;
+       req_sub.id = set->id;
+       for (i = 0; i < set_level; i++)
+               req_sub.ip[i] = set_ip[i];
+       req_sub.level = set_level;
+       req_sub.childsets = options & OPT_CHILDSETS;
+
+       /* Final checks */
+       settype->create_final(settype->data, settype->flags);
+
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_sub) + settype->create_size;
+       data = ipset_malloc(size);
+
+       /* Add up ip_set_req_sub and the settype data */
+       memcpy(data, &req_sub, sizeof(struct ip_set_req_sub));
+       memcpy(data + sizeof(struct ip_set_req_sub),
+              settype->data, settype->create_size);
+
+#ifdef IP_SET_DEBUG
+       /* DEBUG */
+       {
+               void *i;
+
+               DP("CMD_CREATE_CHILD");
+               DP("OP %u", req_sub.op);
+               DP("Id: %u", req_sub.id);
+               DP("All data");
+
+               for (i = data; i < data + size; i += 4) {
+                       unsigned *j = (unsigned *) i;
+                       DP("%x", *j);
+               }
+       }
+#endif
+
+       kernel_sendto(data, size);
+       free(data);
+}
+
+static void set_del(struct set *set)
+{
+       int i;
+       
+       for (i = 0; i < set->levels; i++) {
+               if (set->private[i].setdata)
+                       set->settype[i]->killmembers(&set->private[i].setdata);
+               ipset_free(&set->private[i].bitmap);
+       }
+               
+       free(set);
+}
+
+/* Sends destroy order to kernel for one or all sets
+ * All sets: set == NULL
+ */
+static void set_destroy(struct set *set)
+{
+       struct ip_set_req_std req_destroy;
+
+       req_destroy.op = IP_SET_OP_DESTROY;
+
+       if (set == NULL) {
+               /* Do them all */
+               
+               while (all_sets != NULL) {
+                       set = all_sets;
+                       all_sets = set->next;
+                       req_destroy.id = set->id;
+                       req_destroy.level = 0;
+                       kernel_sendto(&req_destroy,
+                                     sizeof(struct ip_set_req_std));
+                       set_del(set);
+               }
+       } else {
+               int i;
+               
+               DP("destroy %s", set->name);
+
+               /* Only destroy one */
+               req_destroy.id = set->id;
+               for (i = 0; i < set_level; i++)
+                       req_destroy.ip[i] = set_ip[i];
+               req_destroy.level = set_level;  
+               kernel_sendto(&req_destroy,
+                             sizeof(struct ip_set_req_std));
+               if (set_level == 0) {
+                       if (set == all_sets)
+                               all_sets = set->next;
+                       else {
+                               struct set *entry = all_sets;
+                       
+                               while (entry && entry->next && entry->next != set)
+                                       entry = entry->next;
+                               if (entry->next == set)
+                                       entry->next = set->next;
+                       }                               
+                       set_del(set);
+               }
+       }
+}
+
+/* Sends flush order to kernel for one or all sets
+ * All sets: set = NULL
+ */
+static void set_flush(const struct set *set, unsigned options)
+{
+       struct ip_set_req_sub req_flush;
+
+       DP("flush");
+
+       req_flush.op = IP_SET_OP_FLUSH;
+
+       if (set == NULL) {
+               /* Do them all */
+               struct set *entry = all_sets;
+               
+               while (entry != NULL) {
+                       req_flush.id = entry->id;
+                       req_flush.level = 0;
+                       req_flush.childsets = options & OPT_CHILDSETS;
+                       kernel_sendto(&req_flush,
+                                     sizeof(struct ip_set_req_sub));
+                       entry = entry->next;
+               }
+       } else {
+               int i;
+
+               /* Only one */
+               req_flush.id = set->id;
+               for (i = 0; i < set_level; i++)
+                       req_flush.ip[i] = set_ip[i];
+               req_flush.level = set_level;    
+               req_flush.childsets = options & OPT_CHILDSETS;
+               kernel_sendto(&req_flush, sizeof(struct ip_set_req_sub));
+       }
+}
+
+/* Sends rename order to kernel */
+static void set_rename(struct set *set, const char *newname)
+{
+       struct ip_set_req_rename req_rename;
+
+       DP("rename");
+
+       req_rename.op = IP_SET_OP_RENAME;
+       req_rename.id = set->id;
+       strncpy(req_rename.newname, newname, IP_SET_MAXNAMELEN);
+
+       DP("rename - send");
+       kernel_sendto(&req_rename, sizeof(struct ip_set_req_rename));
+       
+       strncpy(set->name, newname, IP_SET_MAXNAMELEN);
+}
+
+/* Sends swap order to kernel for two sets */
+static void set_swap(struct set *from, struct set *to)
+{
+       struct ip_set_req_swap req_swap;
+
+       DP("swap");
+
+       req_swap.op = IP_SET_OP_SWAP;
+       req_swap.id = from->id;
+       req_swap.to = to->id;
+
+       DP("swap - send");
+       kernel_sendto(&req_swap, sizeof(struct ip_set_req_swap));
+       
+       from->id = req_swap.to;
+       to->id = req_swap.id;
+}
+
+static void *list_get(int opsize, int opget, ip_set_ip_t id,
+                     int *size, ip_set_ip_t *ip, unsigned level)
+{
+       int res, i;
+       struct ip_set_req_list req_list;
+       struct ip_set_req_std *req_data;
+       int sockfd = kernel_getsocket();
+       void *data = NULL;
+
+       req_list.op = opsize;
+       req_list.id = id;
+       req_list.level = level;
+       for (i = 0; i < level; i++)
+               req_list.ip[i] = ip[i];
+
+       *size = sizeof(req_list);
+       kernel_getfrom(&req_list, size);
+
+       DP("got size: %d", req_list.size);
+
+       if (req_list.size == 0)
+               return data;
+
+       *size = sizeof(struct ip_set_req_std) > req_list.size ?
+               sizeof(struct ip_set_req_std) : req_list.size;
+
+       data = ipset_malloc(*size);
+       req_data = (struct ip_set_req_std *) data;
+       req_data->op = opget;
+       req_data->id = id;
+       req_data->level = level;
+       for (i = 0; i < level; i++)
+               req_data->ip[i] = ip[i];
+
+       /* Get! */
+       res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+       DP("list_get getsockopt() res=%d errno=%d\n", res, errno);
+
+       if (res != 0)
+               kernel_error(errno);
+
+       /* All OK, return */
+       return data;
+}
+
+static void list_getheaders(struct set *set, ip_set_ip_t *ip, unsigned level)
+{
+       void *data;
+       size_t size;
+
+       data = list_get(IP_SET_OP_LIST_HEADER_SIZE,
+                       IP_SET_OP_LIST_HEADER, 
+                       set->id, &size, ip, level);
+
+       if (size == 0)
+               exit_error(OTHER_PROBLEM,
+                          "Kernel returned zero header size. "
+                          "Something screwed up.");
+                          
+       /* Cleanup setdata */
+       if (set->private[level].setdata)
+               set->settype[level]->killmembers(&set->private[level].setdata);
+
+       /* Handle the data */
+       set->settype[level]->initheader(&set->private[level].setdata,
+                                       data, size);
+
+       ipset_free(&data);
+}
+
+static void list_getmembers(struct set *set, ip_set_ip_t *ip, unsigned level)
+{
+       void *data;
+       size_t size;
+
+       data = list_get(IP_SET_OP_LIST_MEMBERS_SIZE,
+                       IP_SET_OP_LIST_MEMBERS, 
+                       set->id, &size, ip, level);
+                       
+       if (size == 0)
+               exit_error(OTHER_PROBLEM,
+                          "Kernel returned zero header size. "
+                          "Something screwed up.");
+                          
+       /* Handle the data */
+       set->settype[level]->initmembers(set->private[level].setdata,
+                                          data, size);
+
+       /* Next list_getheaders or set_del frees the data */
+}
+
+static void list_getchildsets(struct set *set, ip_set_ip_t *ip, unsigned level)
+{
+       void *data;
+       size_t size;
+
+       data = list_get(IP_SET_OP_LIST_CHILDSETS_SIZE,
+                       IP_SET_OP_LIST_CHILDSETS, 
+                       set->id, &size, ip, level);
+
+       /* Cleanup */
+       ipset_free(&set->private[level].bitmap);
+       set->private[level].bitmap = NULL;
+       
+       if (size == 0)
+               return; /* No child set */
+               
+       /* Handle the data */
+       set->private[level].bitmap = data;
+
+       /* Next list_getchildsets or set_del frees the data */
+}
+
+/* Print a child set */
+static void set_list_childset(struct set *set,
+                             unsigned options,
+                             ip_set_ip_t *ip,
+                             unsigned level)
+{
+       int i;
+       ip_set_ip_t id;
+       
+       /* Load the set header, member and childset data */
+       list_getheaders(set, ip, level);
+       list_getmembers(set, ip, level);
+       list_getchildsets(set, ip, level);
+
+       /* Pretty print the childset */
+       printf("Childset %s %s", 
+              set->private[level].bitmap != NULL ? "+" : "-",
+              set->name);
+       for (i = 0; i < level; i++) {
+               switch (set->settype[i]->typecode) {
+               case IPSET_TYPE_IP:
+                       printf("%s%s", i == 0 ? ":" : ",",
+                                      ip_tostring(ip[i], options));
+                       break;
+               case IPSET_TYPE_PORT:
+                       printf("%s%s", i == 0 ? ":" : ",",
+                                      port_tostring(ip[i], options));
+                       break;
+               default:
+                       ;
+               }
+       }
+       printf("\n");
+
+       /* Pretty print the type header */
+       set->settype[level]->printheader(set->private[level].setdata, options);
+
+       /* Pretty print all IPs */
+       if (options & OPT_SORTED)
+               set->settype[level]->printips_sorted(set->private[level].setdata, options);
+       else
+               set->settype[level]->printips(set->private[level].setdata, options);
+
+       /* Pretty print all childsets. */
+       if (!(set->private[level].bitmap != NULL && (options & OPT_CHILDSETS)))
+               return;
+
+       for (id = 0; 
+            id < set->settype[level]->sizeid(set->private[level].setdata);
+            id++) {
+               if (test_bit(id, set->private[level].bitmap)) {
+                       ip[level] = set->settype[level]->getipbyid(
+                                       set->private[level].setdata, id);
+                       set_list_childset(set, options, ip, level+1);
+               }
+       }
+}
+
+/* Help function to set_list() */
+static void set_list_oneset(struct set *set,
+                             unsigned options)
+{
+       int i;
+       
+       /* Pretty print the set */
+       printf("Name %s\n", set->name);
+       printf("Type %s", set->settype[0]->typename);
+       for (i = 1; i < set->levels; i++)
+               printf(",%s", set->settype[i]->typename);
+       printf("\n");
+       printf("References %d\n", set->ref);
+
+       /* Pretty print the childset */
+       set_list_childset(set, options, set_ip, set_level);
+
+       printf("\n");           /* One newline between sets */
+}
+
+/* Print a set or all sets
+ * All sets: set = NULL
+ */
+static void set_list(struct set *set, unsigned options)
+{
+       if (set == NULL) {
+               set = all_sets;
+               
+               while (set != NULL) {
+                       set_list_oneset(set, options);
+                       set = set->next;
+               }
+       } else
+               set_list_oneset(set, options);
+}
+
+/* Performs a save to stdout
+ * All sets are marked with set.name[0] == '\0'
+ * Area pointed by 'set' will be changed
+ */
+static void set_save(const struct set *set)
+{
+       DP("set_save() not yet implemented");
+}
+
+/* Performs a restore from stdin */
+static void set_restore()
+{
+       DP("set_restore() not yet implemented");
+}
+
+static void parse_adt_ip(int cmd, struct set *set, const char *adt_ip)
+{
+       char *saved = strdup(adt_ip);
+       char *ptr, *tmp = saved;
+
+       ip_level = set_level;
+                       
+       while (ip_level <= set->levels && tmp) {
+
+               if (ip_level > set_level)
+                       list_getheaders(set, set_ip, ip_level);
+
+               ptr = strsep(&tmp, ",");
+
+               /* Call the parser function */
+               set_ip[ip_level] = set->settype[ip_level]->adt_parser(
+                                       cmd, ptr,
+                                       set->private[ip_level].adt,
+                                       set->private[ip_level].setdata);
+               DP("%i: (%s)", ip_level, ip_tostring(set_ip[ip_level], 0));
+               ip_level++;
+       }
+
+       if (tmp || ip_level > set->levels)
+               exit_error(PARAMETER_PROBLEM,
+                          "Specified (child) set and IP levels together"
+                          " are deeper than %s set (%i).", 
+                          set->name, set->levels);
+                          
+       free(saved);
+}
+
+/* Sends addip order to kernel for a set */
+static void set_addip(struct set *set, const char *adt_ip)
+{
+       struct ip_set_req_std req_addip;
+       size_t size, offset;
+       void *data;
+       int i;
+
+       DP("set_addip() %p", set);
+
+       list_getheaders(set, set_ip, set_level);
+
+       parse_adt_ip(ADT_ADD, set, adt_ip);
+
+       req_addip.op = IP_SET_OP_ADD_IP;
+       req_addip.id = set->id;
+       for (i = 0; i < set_level; i++)
+               req_addip.ip[i] = set_ip[i];
+       req_addip.level = set_level;
+
+       DP("%i %i", set_level, ip_level);
+       /* Alloc memory for the data to send */
+       DP("alloc");
+       size = sizeof(struct ip_set_req_std);
+       for (i = set_level; i < ip_level; i++) {
+               size += set->settype[i]->req_size;
+               DP("i: %i, size: %u", i, size);
+       }
+       data = ipset_malloc(size);
+
+       /* Add up req_addip and the settype data */
+       DP("mem");
+       memcpy(data, &req_addip, sizeof(struct ip_set_req_std));
+       offset = sizeof(struct ip_set_req_std);
+       for (i = set_level; i < ip_level; i++) {
+               memcpy(data + offset,
+                      set->private[i].adt,
+                      set->settype[i]->req_size);
+               offset += set->settype[i]->req_size;
+       }
+
+#ifdef IP_SET_DEBUG
+       /* DEBUG */
+       {
+               void *i;
+
+               DP("CMD_ADDIP");
+               DP("OP %u", req_addip.op);
+               DP("Id: %u", req_addip.id);
+               DP("All data");
+
+               for (i = data; i < data + size; i += 4) {
+                       unsigned *j = (unsigned *) i;
+                       DP("%x", *j);
+               }
+       }
+#endif
+
+       if (kernel_sendto_handleexist(data, size) == -1)
+               /* Arghh, we can't get the commandline string anymore, oh well */
+               exit_error(OTHER_PROBLEM, "Already added in set %s.",
+                          set->name);
+       free(data);
+}
+
+/* Sends delip order to kernel for a set */
+static void set_delip(struct set *set, const char *adt_ip, unsigned options)
+{
+       struct ip_set_req_std req_delip;
+       size_t size, offset;
+       void *data;
+       int i;
+
+       DP("set_delip()");
+
+       list_getheaders(set, set_ip, set_level);
+
+       parse_adt_ip(ADT_DEL, set, adt_ip);
+
+       req_delip.op = IP_SET_OP_DEL_IP;
+       req_delip.id = set->id;
+       for (i = 0; i < set_level; i++)
+               req_delip.ip[i] = set_ip[i];
+       req_delip.level = set_level;
+
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_std);
+       for (i = set_level; i < ip_level; i++)
+               size += set->settype[i]->req_size;
+       data = ipset_malloc(size);
+
+       /* Add up req_sub and the settype data */
+       memcpy(data, &req_delip, sizeof(struct ip_set_req_std));
+       offset = sizeof(struct ip_set_req_std);
+       for (i = set_level; i < ip_level; i++) {
+               memcpy(data + offset,
+                      set->private[i].adt,
+                      set->settype[i]->req_size);
+               offset += set->settype[i]->req_size;
+       }
+
+#ifdef IP_SET_DEBUG
+       /* DEBUG */
+       {
+               void *i;
+
+               DP("CMD_DELIP");
+               DP("OP %u", req_delip.op);
+               DP("Id: %u", req_delip.id);
+               DP("All data");
+
+               for (i = data; i < data + size; i += 4) {
+                       unsigned *j = (unsigned *) i;
+                       DP("%x", *j);
+               }
+       }
+#endif
+
+       if (kernel_sendto_handleexist(data, size) == -1)
+               /* Arghh, we can't get the commandline string anymore, oh well */
+               exit_error(OTHER_PROBLEM, "Doesn't exist in set %s.",
+                          set->name);
+       free(data);
+}
+
+/* Sends test order to kernel for a set */
+static int
+set_testip(struct set *set, const char *name, const char *adt_ip)
+{
+       int i, res;
+       struct ip_set_req_test req_test;
+       void *data;
+       size_t size, offset;
+
+       list_getheaders(set, set_ip, set_level);
+
+       parse_adt_ip(ADT_TEST, set, adt_ip);
+
+       req_test.op = IP_SET_OP_TEST_IP;
+       req_test.id = set->id;
+       for (i = 0; i < set_level; i++)
+               req_test.ip[i] = set_ip[i];
+       req_test.level = set_level;
+
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_test);
+       for (i = set_level; i < ip_level; i++)
+               size += set->settype[i]->req_size;
+       data = ipset_malloc(size);
+
+       /* Add up req_test and the settype data */
+       memcpy(data, &req_test, sizeof(struct ip_set_req_test));
+       offset = sizeof(struct ip_set_req_test);
+       for (i = set_level; i < ip_level; i++) {
+               memcpy(data + offset,
+                      set->private[i].adt,
+                      set->settype[i]->req_size);
+               offset += set->settype[i]->req_size;
+       }
+       /* Result in the op-field */
+       kernel_getfrom(data, &size);
+
+       if (size != sizeof(struct ip_set_req_test))
+               exit_error(OTHER_PROBLEM,
+                          "Incorrect return size from kernel."
+                          "Should be %d but was %d.",
+                          sizeof(struct ip_set_req_test), size);
+
+       DP("set_testip() result=%x", req_test.op);
+
+       if (((struct ip_set_req_test *)data)->reply > 0) {
+               ipset_printf("%s is in set %s.", adt_ip, name);
+               res = 0;        /* Return value for the program */
+       } else {
+               ipset_printf("%s is NOT in set %s.", adt_ip, name);
+               res = 1;
+       }
+
+       free(data);
+       return res;
+}
+
+/* Prints help
+ * If settype is non null help for that type is printed as well
+ */
+static void set_help(const struct settype *settype)
+{
+#ifdef IP_SET_DEBUG
+       char debughelp[] =
+              "  --debug      -z              Enable debugging\n\n";
+#else
+       char debughelp[] = "\n";
+#endif
+
+       printf("%s v%s\n\n"
+              "Usage: %s -N new-set settype [options]\n"
+              "       %s -[XFLSH] [set] [options]\n"
+              "       %s -[EW] from-set to-set\n"
+              "       %s -[ADT] set entry\n"
+              "       %s -R\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);
+
+       printf("Commands:\n"
+              "Either long or short options are allowed.\n"
+              "  --create  -N setname settype0[,settype1,...] <options>\n"
+              "                    Create a new set\n"
+              "  --create  -N setname:IP[,IP...] settype <options>\n"
+              "                    Create childset at setname:IP[,IP...]\n"
+              "  --destroy -X [setname:IP,....]\n"
+              "                    Destroy a (child)set or all sets\n"
+              "  --flush   -F [setname:IP,...] [options]\n"
+              "                    Delete a (child)set or all sets\n"
+              "  --rename  -E from-set to-set\n"
+              "                    Rename from-set to to-set\n"
+              "  --swap    -W from-set to-set\n"
+              "                    Swap the content of two existing sets\n"
+              "  --list    -L [setname:IP,...] [options]\n"
+              "                    List the entries in a (child)set or all sets\n"
+              "  --save    -S [setname]\n"
+              "                    Save the set or all sets to stdout\n"
+              "  --restore -R\n"
+              "                    Restores from stdin a saved state\n"
+              "  --add     -A setname[:IP,...] entry[,entry...]\n"
+              "                    Add an entry to a (child)set\n"
+              "  --del     -D setname[:IP,...] entry[,entry...]\n"
+              "                    Deletes an entry from a (child)set\n"
+              "  --test    -T setname[:IP,...] entry[,entry...]\n"
+              "                    Tests if an entry exists in a (child)set.\n"
+              "  --help    -H [settype] [options]]\n"
+              "                    Prints this help, and settype specific help\n"
+              "  --version -V\n"
+              "                    Prints version information\n\n"
+              "Options:\n"
+              "  --sorted     -s   Numeric sort of the IPs in -L\n"
+              "  --numeric    -n   Numeric output of addresses in a -L\n"
+              "  --quiet      -q   Suppress any output to stdout and stderr.\n"
+              "  --childsets  -c   Operation valid for child sets\n"
+              "  --hint       -i   Hint best settype initialization parameters\n");
+       printf(debughelp);
+
+       if (settype != NULL) {
+               printf("Type '%s' specific:\n", settype->typename);
+               settype->usage();
+       }
+}
+
+/* Hint various infos on a given settype */
+static int
+settype_hint(const struct settype *settype, unsigned options)
+{
+       char buffer[1024];
+       ip_set_ip_t ip[MAX_HINT_SIZE];
+       ip_set_ip_t id = 0;
+       
+       if (!settype->hint)
+               return 0;
+       
+       while (fgets(buffer, sizeof(buffer), stdin) != NULL
+              && id < MAX_HINT_SIZE) {
+               if (buffer[0] == '\n' || buffer[0] == '#')
+                       continue;
+               switch (settype->typecode) {
+               case IPSET_TYPE_IP:
+                       parse_ip(buffer, &ip[id++]);
+                       break;
+               case IPSET_TYPE_PORT:
+                       parse_port(buffer, &ip[id++]);
+                       break;
+               default:
+                       ;
+               }
+       }
+       if (id >= MAX_HINT_SIZE)
+               exit_error(OTHER_PROBLEM,
+                          "More than %ld entries, exiting.",
+                          MAX_HINT_SIZE);
+       if (id < 1)
+               exit_error(OTHER_PROBLEM,
+                          "No input, no hint.");
+
+       settype->hint(settype->data, ip, id);
+       
+       return 0;
+}
+
+static void init_sets(void)
+{
+       int version;
+       static int done = 0;
+       
+       if (done)
+               return;
+       
+       version = get_protocolversion();
+       if (version != IP_SET_PROTOCOL_VERSION)
+               exit_error(OTHER_PROBLEM,
+                          "Kernel ipset code is of protocol version %u."
+                          "I'm of protocol version %u.\n"
+                          "Please upgrade your kernel and/or ipset(8) utillity.",
+                          version, IP_SET_PROTOCOL_VERSION);
+
+       /* Get the list of existing sets from the kernel */
+       get_sets();
+       done = 1;
+}
+
+/* Main worker function */
+int parse_commandline(int argc, char *argv[], int exec_restore)
+{
+       int res = 0;
+       unsigned command = 0;
+       unsigned options = 0;
+       int c;
+
+       struct set *set = NULL;
+       struct set *set_to = NULL;              /* Used by -W */
+       struct settype *settype = NULL;         /* Used by -H */
+       char *name = NULL;                      /* Used by -E */
+       char *entries = NULL;                   /* Used by -A, -D, -T */
+       
+       struct option *opts = opts_long;
+
+       /* Suppress error messages: we may add new options if we
+          demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv, opts_short, opts, NULL)) != -1) {
+
+               DP("commandline parsed: opt %c (%s)", c, argv[optind]);
+
+               switch (c) {
+                       /*
+                        * Command selection.
+                        */
+               case 'h':
+               case 'H':{      /* Help */
+                               set_command(&command, CMD_HELP);
+                               
+                               if (optarg)
+                                       set_checktype(optarg);
+                               else if (optind < argc
+                                        && argv[optind][0] != '-')
+                                       set_checktype(argv[optind++]);
+
+                               if (!set_typename_level)
+                                       break;
+                                       
+                               if (set_typename_level != 1)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires one settype as argument",
+                                                  cmd2char(CMD_HELP));
+                               
+                               settype = settype_load(set_typename[0]);
+
+                               /* Merge the hint options */
+                               if (settype->hint_parse) {
+                                       opts = merge_options(opts,
+                                                    settype->hint_opts,
+                                                    &settype->option_offset);
+                                                               
+                                       /* Reset space for settype create data */
+                                       memset(settype->data, 0, settype->hint_size);
+
+                                       /* Zero the flags */
+                                       settype->flags = 0;
+
+                                       DP("call hint_init");
+                                       /* Call the settype hint_init */
+                                       settype->hint_init(settype->data);
+                               }
+                               
+                               break;
+                       }
+
+               case 'V':{      /* Version */
+                               /* Dont display kernel protocol version because 
+                                * that might generate errors if the ipset module 
+                                * is not loaded in.*/
+                               printf("%s v%s Protocol version %u.\n",
+                                      program_name, program_version,
+                                      IP_SET_PROTOCOL_VERSION);
+                               exit(0);
+                       }
+                       
+               case 'N':{      /* Create */
+                               char *typename = NULL;
+                               int i;
+                               
+                               init_sets();
+                               
+                               DP("check setname");
+                               /* setname */
+                               set = set_checkname(optarg);
+                               
+                               if (set_level == 0) {
+                                       /* New set to be created */
+
+                                       DP(" new set, set_level == 0");
+
+                                       if (set != NULL) 
+                                               exit_error(OTHER_PROBLEM,
+                                                          "Set %s already exists.",
+                                                          set_name);
+                                       set = ipset_malloc(sizeof(struct set));
+                                       memset(set, 0, sizeof(struct set));
+                                       
+                                       set_command(&command, CMD_CREATE);
+
+                                       /* typename */
+                                       if (optind < argc
+                                           && argv[optind][0] != '-')
+                                               typename = argv[optind++];
+                                       else
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "-%c requires new-setname and settype",
+                                                          cmd2char(CMD_CREATE));
+
+                                       DP(" check typename");
+                                       set_checktype(typename);
+                                       
+                                       strcpy(set->name, set_name);
+                                       set->levels = set_typename_level;
+
+                                       DP("load the settypes");
+                                       for (i = 0; i < set_typename_level; i++)
+                                               set->settype[i] = settype_load(set_typename[i]);
+
+                               } else {/* set_level != 0 */
+                                       /* New childset to be created */
+
+                                       DP(" new childset, set_level != 0");
+
+                                       if (set == NULL) 
+                                               exit_error(OTHER_PROBLEM,
+                                                          "Set %s does not exist.",
+                                                          set_name);
+                                       set_command(&command, CMD_CREATE_CHILD);
+
+                                       /* typename */
+                                       if (optind < argc
+                                           && argv[optind][0] != '-')
+                                               typename = argv[optind++];
+                                       else
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "-%c requires new-setname and settype",
+                                                          cmd2char(CMD_CREATE));
+
+                                       DP(" check typename");
+                                       set_checktype(typename);
+                                       if (set_typename_level > 1)
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "-%c requires single settype specified",
+                                                          cmd2char(CMD_CREATE));
+                                       else if (set_level >= set->levels)
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "specified childset is deeper than "
+                                                          "%s set itself.", set->name);
+                                       else if (strcmp(typename, set->settype[set_level]->typename))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "settype '%s' must be used instead of "
+                                                          "'%s' in childset '%s'",
+                                                          set->settype[set_level]->typename,
+                                                          typename, optarg);
+                               }
+
+                               DP("merge options");
+                               /* Merge the create options */
+                               opts = merge_options(opts,
+                                            set->settype[set_level]->create_opts,
+                                            &set->settype[set_level]->option_offset);
+
+                               /* Reset space for settype create data */
+                               memset(set->settype[set_level]->data, 0,
+                                      set->settype[set_level]->create_size);
+
+                               /* Zero the flags */
+                               set->settype[set_level]->flags = 0;
+
+                               DP("call create_init");
+                               /* Call the settype create_init */
+                               set->settype[set_level]->create_init(
+                                               set->settype[set_level]->data);
+
+                               break;
+                       }
+
+               case 'X':{      /* Destroy */
+                               init_sets();
+                               
+                               set_command(&command, CMD_DESTROY);
+
+                               if (optarg)
+                                       set = set_find_byname(optarg);
+                               else if (optind < argc
+                                          && argv[optind][0] != '-')
+                                       set = set_find_byname(argv[optind++]);
+                               else
+                                       set = NULL;     /* Mark to destroy all (empty) sets */
+
+                               if (set && set_level >= set->levels)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "specified childset is deeper than "
+                                                  "%s set itself.", set->name);
+                               break;
+                       }
+
+               case 'F':{      /* Flush */
+                               init_sets();
+                               
+                               set_command(&command, CMD_FLUSH);
+
+                               DP("flush: %s", optarg);
+
+                               if (optarg)
+                                       set = set_find_byname(optarg);
+                               else if (optind < argc
+                                          && argv[optind][0] != '-')
+                                       set = set_find_byname(argv[optind++]);
+                               else
+                                       set = NULL;     /* Mark to flush all */
+
+                               if (set && set_level >= set->levels)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "specified childset is deeper than "
+                                                  "%s set itself.", set->name);
+                               break;
+                       }
+
+               case 'E':{      /* Rename */
+                               init_sets();
+                               
+                               set_command(&command, CMD_RENAME);
+
+                               set = set_find_byname(optarg);
+                               if (set_level)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "childsets cannot be swapped");
+
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       name = argv[optind++];
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires a setname "
+                                                  "and the new name for that set",
+                                                  cmd2char(CMD_RENAME));
+                               if (strlen(name) > IP_SET_MAXNAMELEN - 1)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Setname '%s' is too long. Max %d characters.",
+                                                  name, IP_SET_MAXNAMELEN - 1);
+
+                               /* Set with new name must not exist. */
+                               set_to = set_checkname(name);
+                               if (set_to)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Set already exists, cannot rename to %s",
+                                                  name);
+                               if (set_level)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "childsets cannot be swapped");
+
+                               break;
+                       }
+
+               case 'W':{      /* Swap */
+                               char *name = NULL;
+
+                               init_sets();
+                               
+                               set_command(&command, CMD_SWAP);
+
+                               set = set_find_byname(optarg);
+                               if (set_level)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "childsets cannot be swapped");
+
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       name = argv[optind++];
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires the names of two "
+                                                  "existing sets",
+                                                  cmd2char(CMD_SWAP));
+                               if (strlen(name) > IP_SET_MAXNAMELEN - 1)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Setname '%s' is too long. Max %d characters.",
+                                                  name, IP_SET_MAXNAMELEN - 1);
+
+                               /* Both sets must exist. */
+                               set_to = set_find_byname(name);
+                               if (set_level)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "childsets cannot be swapped");
+
+                               break;
+                       }
+
+               case 'L':{      /* List */
+                               init_sets();
+                               
+                               set_command(&command, CMD_LIST);
+                               if (optarg)
+                                       set = set_find_byname(optarg);
+                               else if (optind < argc
+                                          && argv[optind][0] != '-')
+                                       set = set_find_byname(argv[optind++]);
+                               else
+                                       set = NULL;     /* Mark all */
+
+                               if (set && set_level >= set->levels)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "specified childset is deeper than "
+                                                  "%s set itself.", set->name);
+                               break;
+                       }
+
+               case 'S':{      /* Save */
+                               init_sets();
+                               
+                               set_command(&command, CMD_SAVE);
+                               if (optarg)
+                                       set = set_find_byname(optarg);
+                               else if (optind < argc
+                                          && argv[optind][0] != '-')
+                                       set = set_find_byname(argv[optind++]);
+                               else
+                                       set = NULL;     /* Mark to save all */
+
+                               if (set && set_level >= set->levels)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "specified childset is deeper than "
+                                                  "%s set itself.", set->name);
+                               break;
+                       }
+
+               case 'R':{      /* Restore */
+                               init_sets();
+                               
+                               set_command(&command, CMD_RESTORE);
+                               break;
+                       }
+
+               case 'A':{      /* Add IP */
+                               init_sets();
+                               
+                               set_command(&command, CMD_ADD);
+
+                               set = set_find_byname(optarg);
+
+                               if (set_level >= set->levels)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "specified childset is deeper than "
+                                                  "%s set itself.", set->name);
+
+                               /* entries */
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       entries = argv[optind++];
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires setname and entries",
+                                                  cmd2char(CMD_ADD));
+
+                               break;
+                       }
+
+               case 'D':{      /* Del IP */
+                               init_sets();
+                               
+                               set_command(&command, CMD_DEL);
+
+                               set = set_find_byname(optarg);
+
+                               if (set_level >= set->levels)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "specified childset is deeper than "
+                                                  "%s set itself.", set->name);
+
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       entries = argv[optind++];
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires setname and entries",
+                                                  cmd2char(CMD_DEL));
+
+                               break;
+                       }
+
+               case 'T':{      /* Test IP */
+                               init_sets();
+                               
+                               set_command(&command, CMD_TEST);
+
+                               set = set_find_byname(optarg);
+                               name = optarg;
+
+                               if (set_level >= set->levels)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "specified childset is deeper than "
+                                                  "%s set itself.", set->name);
+
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       entries = argv[optind++];
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires setname and entries",
+                                                  cmd2char(CMD_TEST));
+
+                               break;
+                       }
+
+                       /* options */
+
+               case 'n':
+                       add_option(&options, OPT_NUMERIC);
+                       break;
+
+               case 's':
+                       add_option(&options, OPT_SORTED);
+                       break;
+
+               case 'q':
+                       add_option(&options, OPT_QUIET);
+                       option_quiet = 1;
+                       break;
+
+#ifdef IP_SET_DEBUG
+               case 'z':       /* debug */
+                       add_option(&options, OPT_DEBUG);
+                       option_debug = 1;
+                       break;
+#endif
+
+               case 'c':
+                       add_option(&options, OPT_CHILDSETS);
+                       break;
+
+               case 'i':
+                       add_option(&options, OPT_HINT);
+                       break;
+
+               case 1: /* non option */
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+                       break;  /*always good */
+
+               default:{
+                               DP("default");
+
+                               switch (command) {
+                               case CMD_CREATE:
+                                       res = set->settype[set_level]->create_parse(
+                                                       c - set->settype[set_level]->option_offset,
+                                                       argv,
+                                                       set->settype[set_level]->data,
+                                                       &set->settype[set_level]->flags);
+                                       break;
+
+                               case CMD_CREATE_CHILD:
+                                       res = set->settype[set_level]->create_parse(
+                                                       c - set->settype[set_level]->option_offset,
+                                                       argv,
+                                                       set->settype[set_level]->data,
+                                                       &set->settype[set_level]->flags);
+                                       break;
+
+                               case CMD_HELP: {
+                                       if (!(settype && settype->hint_parse))
+                                               break;
+                                                       
+                                       res = settype->hint_parse(
+                                                       c - settype->option_offset,
+                                                       argv,
+                                                       settype->data);
+                                       break;
+                                       }
+                               default:
+                                       res = 0;        /* failed */
+                               }       /* switch (command) */
+
+
+                               if (!res)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind - 1]);
+
+                       }
+
+                       DP("next arg");
+               }       /* switch */
+
+       }       /* while( getopt_long() ) */
+
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+
+       /* Check options */
+       generic_opt_check(command == CMD_CREATE_CHILD ? CMD_CREATE : command, options);
+
+       DP("cmd: %c", cmd2char(command));
+
+       switch (command) {
+       case CMD_CREATE:
+               DP("CMD_CREATE");
+               set_create(set);
+               break;
+
+       case CMD_CREATE_CHILD:
+               DP("CMD_CREATE_CHILD");
+               set_create_childset(set, options);
+               break;
+
+       case CMD_DESTROY:
+               set_destroy(set);
+               break;
+
+       case CMD_FLUSH:
+               set_flush(set, options);
+               break;
+
+       case CMD_RENAME:
+               set_rename(set, name);
+               break;
+
+       case CMD_SWAP:
+               set_swap(set, set_to);
+               break;
+
+       case CMD_LIST:
+               set_list(set, options);
+               break;
+
+       case CMD_SAVE:
+               set_save(set);
+               break;
+
+       case CMD_RESTORE:
+               set_restore();
+               break;
+
+       case CMD_ADD:
+               set_addip(set, entries);
+               break;
+
+       case CMD_DEL:
+               set_delip(set, entries, options);
+               break;
+
+       case CMD_TEST:
+               res = set_testip(set, name, entries);
+               break;
+
+       case CMD_HELP:
+               if (options & OPT_HINT)
+                       res = settype_hint(settype, options);
+               else
+                       set_help(settype);
+               break;
+
+       default:
+               /* Will never happen */
+               ; /* Keep the compiler happy */
+
+       }       /* switch( command ) */
+
+       return res;
+}
+
+
+int main(int argc, char *argv[])
+{
+       return parse_commandline(argc, argv, 0);
+
+}
diff --git a/ipset.h b/ipset.h
new file mode 100644 (file)
index 0000000..095f756
--- /dev/null
+++ b/ipset.h
@@ -0,0 +1,203 @@
+#ifndef __IPSET_H
+#define __IPSET_H
+
+/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ *                     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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <getopt.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+#include <linux/netfilter_ipv4/ip_set.h>
+
+char program_name[] = "ipset";
+char program_version[] = "1.0";
+
+#define IPSET_LIB_NAME "/libipset_%s.so"
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+
+#define LIST_TRIES 5
+
+/* FIXME: move this to Makefile ? */
+#if 1
+#define IP_SET_DEBUG
+#endif
+
+#ifdef IP_SET_DEBUG
+extern int option_debug;
+#define DP(format, args...) if (option_debug)                  \
+       do {                                                    \
+               fprintf(stderr, "%s: %s (DBG): ", __FILE__, __FUNCTION__);\
+               fprintf(stderr, format "\n" , ## args);                 \
+       } while (0)
+#else
+#define DP(format, args...)
+#endif
+
+enum exittype {
+       OTHER_PROBLEM = 1,
+       PARAMETER_PROBLEM,
+       VERSION_PROBLEM
+};
+
+struct set_data {
+       void *setdata;                  /* Hook to set speficic data */
+       void *bitmap;                   /* Which elements has got a childset (bitmap) */
+       void *adt;                      /* Add/del/test data */
+};
+
+/* The simplistic view of an ipset */
+struct set {
+       struct set *next;
+
+       char name[IP_SET_MAXNAMELEN];           /* Name of the set */
+       unsigned id;                            /* Pool id in kernel */
+       unsigned levels;                        /* Levels we may have in this set */
+       unsigned ref;                           /* References in kernel */
+       struct settype *settype[IP_SET_LEVELS]; /* Pointer to set type functions */
+       struct set_data private[IP_SET_LEVELS]; /* Hook to set specific data */
+};
+
+#define ADT_ADD                0
+#define ADT_DEL                1
+#define ADT_TEST       2
+
+struct settype {
+       struct settype *next;
+
+       char typename[IP_SET_MAXNAMELEN];
+       char typecode;
+
+       int protocol_version;
+
+       /*
+        * Create set
+        */
+
+       /* Size of create data. Will be sent to kernel */
+       size_t create_size;
+
+       /* Initialize the create. */
+       void (*create_init) (void *data);
+
+       /* Function which parses command options; returns true if it ate an option */
+       int (*create_parse) (int c, char *argv[], void *data,
+                            unsigned *flags);
+
+       /* Final check; exit if not ok. */
+       void (*create_final) (void *data, unsigned int flags);
+
+       /* Pointer to list of extra command-line options for create */
+       struct option *create_opts;
+
+
+       /*
+        * Add/del/test IP
+        */
+
+       /* Size of data. Will be sent to kernel */
+       size_t req_size;
+
+       /* Function which parses command options */
+       ip_set_ip_t (*adt_parser) (int cmd, const char *option, 
+                                  void *data, const void * setdata);
+
+       /*
+        * Printing
+        */
+
+       /* Set up the sets headerinfo with data coming from kernel */
+       void (*initheader) (void ** setdata, void *data, size_t len);
+
+       /* Set up the sets members with data coming from kernel */
+       void (*initmembers) (void * setdata, void *data, size_t len);
+
+       /* Remove the members memory usage */
+       void (*killmembers) (void ** setdata);
+
+       /* Pretty print the type-header */
+       void (*printheader) (const void * setdata, unsigned options);
+
+       /* Returns an IP address from the set */
+       ip_set_ip_t (*getipbyid) (const void * setdata, ip_set_ip_t id);
+       
+       /* Return the size of the set in ids */
+       ip_set_ip_t (*sizeid) (const void * setdata);
+       
+       /* Pretty print all IPs */
+       void (*printips) (const void * setdata, unsigned options);
+
+       /* Pretty print all IPs sorted */
+       void (*printips_sorted) (const void * setdata,
+                                unsigned options);
+
+       /* Print save arguments for creating the set */
+       void (*saveheader) (const void * setdata);
+
+       /* Print save for all IPs */
+       void (*saveips) (const void * setdata);
+
+       /* Print usage */
+       void (*usage) (void);
+
+       /*
+        * Hint: size of data must be smaller than that of create!
+        */
+
+       /* Size of hint data. Will *not* be sent to kernel */
+       size_t hint_size;
+
+       /* Initialize the hint data. */
+       void (*hint_init) (void *data);
+
+       /* Function which parses command options; returns true if it ate an option */
+       int (*hint_parse) (int c, char *argv[], void *data);
+
+       /* Pointer to list of extra command-line options for hinting */
+       struct option *hint_opts;
+
+       /* Hint initialization info */
+       void (*hint) (const void *data, ip_set_ip_t * ip, ip_set_ip_t id);
+
+       /* Internal data */
+       unsigned int option_offset;
+       unsigned int flags;
+       void *data;
+};
+
+extern void settype_register(struct settype *settype);
+
+/* extern void unregister_settype(set_type_t *set_type); */
+
+extern void exit_error(enum exittype status, char *msg, ...);
+
+extern char *ip_tostring(ip_set_ip_t ip, unsigned options);
+extern void parse_ip(const char *str, ip_set_ip_t * ip);
+extern void parse_mask(const char *str, ip_set_ip_t * mask);
+extern void parse_ipandmask(const char *str, ip_set_ip_t * ip,
+                           ip_set_ip_t * mask);
+extern char *port_tostring(ip_set_ip_t port, unsigned options);
+extern void parse_port(const char *str, ip_set_ip_t * port);
+extern int string_to_number(const char *str, unsigned int min, unsigned int max,
+                           ip_set_ip_t *port);
+
+extern void *ipset_malloc(size_t size);
+extern void ipset_free(void **data);
+
+#endif /* __IPSET_H */
diff --git a/ipset_iphash.c b/ipset_iphash.c
new file mode 100644 (file)
index 0000000..ea18296
--- /dev/null
@@ -0,0 +1,477 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+#include <asm/types.h>
+
+#include <linux/netfilter_ipv4/ip_set_iphash.h>
+#include <linux/netfilter_ipv4/ip_set_jhash.h>
+
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_INITVAL    0x01U
+#define OPT_CREATE_HASHSIZE   0x02U
+#define OPT_CREATE_NETMASK    0x03U
+
+#define OPT_HINT_TRY         0x01U
+#define OPT_HINT_NETMASK      0x02U
+
+#define HINT_DEFAULT_TRY       8
+#define HINT_DEFAULT_FACTOR    4
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+
+       DP("create INIT");
+       
+       mydata->initval = 0;
+       mydata->netmask = 0xFFFFFFFF;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+       char *end;
+       unsigned int bits;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               errno = 0;
+               mydata->initval = strtoul(optarg, &end, 0);
+               if (*end == '\0' && end != optarg && errno != ERANGE) {
+
+                       *flags |= OPT_CREATE_INITVAL;
+
+                       DP("--initval 0x%x)", mydata->initval);
+                       break;
+               }
+               exit_error(PARAMETER_PROBLEM, "Invalid initval `%s' specified", optarg);
+       case '2':
+
+               if (string_to_number(optarg, 1, MAX_RANGE, &mydata->hashsize))
+                       exit_error(PARAMETER_PROBLEM, "Hashsize `%s' specified", optarg);
+
+               *flags |= OPT_CREATE_HASHSIZE;
+
+               DP("--hashsize %u", mydata->hashsize);
+               
+               break;
+
+       case '3':
+
+               if (string_to_number(optarg, 0, 32, &bits))
+                       exit_error(PARAMETER_PROBLEM, 
+                                 "Invalid netmask `%s' specified", optarg);
+               
+               if (bits != 0)
+                       mydata->netmask = 0xFFFFFFFF << (32 - bits);
+
+               *flags |= OPT_CREATE_NETMASK;
+
+               DP("--netmask %x", mydata->netmask);
+               
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+
+       if ((flags & OPT_CREATE_HASHSIZE) == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --hashsize\n");
+
+       if (!mydata->initval) {
+               srand(getpid() | time(NULL));
+               mydata->initval = rand();
+       }
+       DP("initval: 0x%x hashsize %d", mydata->initval, mydata->hashsize);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"initval", 1, 0, '1'},
+       {"hashsize", 1, 0, '2'},
+       {"netmask", 1, 0, '3'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(int cmd, const char *optarg, 
+                      void *data, const void *setdata)
+{
+       struct ip_set_req_iphash *mydata =
+           (struct ip_set_req_iphash *) data;
+
+       mydata->flags = 0;
+       
+       if (*optarg == '+') {
+               if (cmd == ADT_ADD) {
+                       mydata->flags |= IPSET_ADD_OVERWRITE;
+                       optarg++;
+               } else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "The '!' overwrite flag can be used only "
+                                  "when adding an IP address to the set\n");
+       }                       
+       parse_ip(optarg, &mydata->ip);
+
+       return mydata->ip;      
+};
+
+ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) setdata;
+
+       return mysetdata->members[id];
+}
+
+ip_set_ip_t sizeid(const void *setdata)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) setdata;
+
+       return (mysetdata->hashsize);
+}
+
+void initheader(void **setdata, void *data, size_t len)
+{
+       struct ip_set_req_iphash_create *header =
+           (struct ip_set_req_iphash_create *) data;
+
+       DP("iphash: initheader() 1");
+
+       if (len != sizeof(struct ip_set_req_iphash_create))
+               exit_error(OTHER_PROBLEM,
+                          "Iphash: incorrect size of header. "
+                          "Got %d, wanted %d.", len,
+                          sizeof(struct ip_set_req_iphash_create));
+
+       *setdata = ipset_malloc(sizeof(struct ip_set_iphash));
+
+       DP("iphash: initheader() 2");
+
+       ((struct ip_set_iphash *) *setdata)->initval =
+               header->initval;
+       ((struct ip_set_iphash *) *setdata)->hashsize =
+               header->hashsize;
+       ((struct ip_set_iphash *) *setdata)->netmask =
+               header->netmask;
+}
+
+void initmembers(void *setdata, void *data, size_t len)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) setdata;
+       size_t size;
+
+       DP("iphash: initmembers()");
+
+       /* Check so we get the right amount of memberdata */
+       size = sizeof(ip_set_ip_t) * mysetdata->hashsize;
+
+       if (len != size)
+               exit_error(OTHER_PROBLEM,
+                          "Iphash: incorrect size of members. "
+                          "Got %d, wanted %d.", len, size);
+
+       mysetdata->members = data;
+}
+
+void killmembers(void **setdata)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) *setdata;
+
+       DP("iphash: killmembers()");
+
+       if (mysetdata->members != NULL)
+               free(mysetdata->members);
+               
+       ipset_free(setdata);
+}
+
+unsigned int
+mask_to_bits(ip_set_ip_t mask)
+{
+       unsigned int bits = 32;
+       ip_set_ip_t maskaddr;
+       
+       if (mask == 0xFFFFFFFF)
+               return bits;
+       
+       maskaddr = 0xFFFFFFFE;
+       while (--bits >= 0 && maskaddr != mask)
+               maskaddr <<= 1;
+       
+       return bits;
+}
+       
+void printheader(const void *setdata, unsigned options)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) setdata;
+
+       printf(" initval: 0x%x", mysetdata->initval);
+       printf(" hashsize: %d", mysetdata->hashsize);
+       if (mysetdata->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask));
+}
+
+void printips(const void *setdata, unsigned options)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) setdata;
+       ip_set_ip_t id;
+
+       for (id = 0; id < mysetdata->hashsize; id++)
+               if (mysetdata->members[id])
+                       printf("%s\n", ip_tostring(mysetdata->members[id], options));
+}
+
+void saveheader(const void *setdata)
+{
+       return;
+}
+
+/* Print save for an IP */
+void saveips(const void *setdata)
+{
+       return;
+}
+
+void usage(void)
+{
+       printf
+           ("-N set iphash [--initval hash-initval] --hashsize hashsize [--netmask CIDR-netmask]\n"
+            "-A set [!]IP\n"
+            "-D set IP\n"
+            "-T set IP\n"
+            "-H iphash -i [--try number] [--factor number] [--netmask CIDR-netmask]\n");
+}
+
+struct ip_set_iphash_hint {
+       unsigned int try;
+       unsigned int factor;
+       ip_set_ip_t netmask;
+};
+
+/* Initialize the hint. */
+void hint_init(void *data)
+{
+       struct ip_set_iphash_hint *mydata = 
+               (struct ip_set_iphash_hint *) data;
+
+       DP("hint INIT");
+       
+       mydata->try = HINT_DEFAULT_TRY;
+       mydata->factor = HINT_DEFAULT_FACTOR;
+       mydata->netmask = 0xFFFFFFFF;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int hint_parse(int c, char *argv[], void *data)
+{
+       struct ip_set_iphash_hint *mydata = 
+               (struct ip_set_iphash_hint *) data;
+       unsigned int bits;
+
+       DP("hint_parse");
+
+       switch (c) {
+       case '1':
+               if (string_to_number(optarg, 1, 32, &mydata->try))
+                       exit_error(PARAMETER_PROBLEM, 
+                                 "Invalid --try `%s' specified (out of range 1-32", optarg);
+               
+               DP("--try %i)", mydata->try);
+               break;
+
+       case '2':
+               if (string_to_number(optarg, 1, 64, &mydata->factor))
+                       exit_error(PARAMETER_PROBLEM, 
+                                 "Invalid --factor `%s' specified (out of range 1-64", optarg);
+               
+               DP("--factor %i)", mydata->factor);
+               break;
+
+       case '3':
+
+               if (string_to_number(optarg, 0, 32, &bits))
+                       exit_error(PARAMETER_PROBLEM, 
+                                 "Invalid netmask `%s' specified", optarg);
+               
+               if (bits != 0)
+                       mydata->netmask = 0xFFFFFFFF << (32 - bits);
+
+               DP("--netmask %x", mydata->netmask);
+               
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Hint commandline options */
+static struct option hint_opts[] = {
+       {"try", 1, 0, '1'},
+       {"factor", 1, 0, '2'},
+       {"netmask", 1, 0, '3'},
+       {0}
+};
+
+int hint_try(const ip_set_ip_t *ip, ip_set_ip_t *test, ip_set_ip_t best,
+            ip_set_ip_t hashsize, unsigned int try, uint32_t *initval, int *found)
+{
+       ip_set_ip_t id, hash;
+       int i = 0;
+
+    next_try:
+       while (i < try) {
+               memset(test, 0, MAX_RANGE * sizeof(ip_set_ip_t));
+               for (id = 0; id < best; id++) {
+                       hash = jhash_1word(ip[id], *initval) % hashsize;
+                       if (test[hash] != 0) {
+                               *initval = rand();
+                               i++;
+                               goto next_try;
+                       } else
+                               test[hash] = ip[id];
+                       DP("%i %x", hash, ip[id]);
+               }
+               printf("--initval 0x%08x --hashsize %d\n", *initval, hashsize);
+               *found = 1;
+               return 1;
+       }
+       return 0;       
+}
+
+void hint(const void *data, ip_set_ip_t *ip, ip_set_ip_t best)
+{
+       struct ip_set_iphash_hint *mydata = 
+               (struct ip_set_iphash_hint *) data;
+       uint32_t initval;
+       ip_set_ip_t next, prev, curr;
+       ip_set_ip_t test[MAX_RANGE];
+       ip_set_ip_t id;
+       int found = 0;
+       
+       curr = MAX_RANGE > mydata->factor * best ? mydata->factor * best : MAX_RANGE;
+       prev = next = MAX_RANGE;
+
+       srand(curr);
+       initval = rand();
+
+       for (id = 0; id < best; id++)
+               ip[id] &= mydata->netmask;
+
+       while (1) {
+               DP("%u %u %u %u", prev, curr, next, best);
+
+               if (hint_try(ip, test, best, curr, mydata->try, &initval, &found)) {
+                       if (curr == best)
+                               return;
+                       next = (curr + best)/2;
+               } else {
+                       if (curr == prev){
+                               if (!found)
+                                       printf("Cannot find good init values.\n");
+                               return;
+                       }
+                       next = (curr + prev)/2;
+               }
+               prev = curr;
+               curr = next;
+       }
+}
+static struct settype settype_iphash = {
+       .typename = SETTYPE_NAME,
+       .typecode = IPSET_TYPE_IP,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_iphash_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .req_size = sizeof(struct ip_set_req_iphash),
+       .adt_parser = &adt_parser,
+
+       /* Get an IP address by id */
+       .getipbyid = &getipbyid,
+       .sizeid = &sizeid,
+
+       /* Printing */
+       .initheader = &initheader,
+       .initmembers = &initmembers,
+       .killmembers = &killmembers,
+       .printheader = &printheader,
+       .printips = &printips,          /* We only have the unsorted version */
+       .printips_sorted = &printips,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       .usage = &usage,
+
+       /* Hint */
+       .hint_size = sizeof(struct ip_set_iphash_hint),
+       .hint_init = &hint_init,
+       .hint_parse = &hint_parse,
+       .hint_opts = hint_opts,
+       .hint = &hint,
+};
+
+void _init(void)
+{
+       settype_register(&settype_iphash);
+
+}
diff --git a/ipset_ipmap.c b/ipset_ipmap.c
new file mode 100644 (file)
index 0000000..df03a14
--- /dev/null
@@ -0,0 +1,384 @@
+/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ *                     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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+
+#include <linux/netfilter_ipv4/ip_set_ipmap.h>
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+#define OPT_CREATE_NETWORK 0x04U
+#define OPT_CREATE_NETMASK 0x08U
+
+#define OPT_ADDDEL_IP      0x01U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+
+       DP("create INIT");
+       mydata->netmask = 0xFFFFFFFF;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+       unsigned int bits;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_ip(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  ip_tostring(mydata->from, 0));
+
+               break;
+
+       case '2':
+               parse_ip(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to, ip_tostring(mydata->to, 0));
+
+               break;
+
+       case '3':
+               parse_ipandmask(optarg, &mydata->from, &mydata->to);
+
+               /* Make to the last of from + mask */
+               mydata->to = mydata->from | ~(mydata->to);
+
+               *flags |= OPT_CREATE_NETWORK;
+
+               DP("--network from %x (%s)", mydata->from,
+                  ip_tostring(mydata->from, 0));
+               DP("--network to   %x (%s)", mydata->to,
+                  ip_tostring(mydata->to, 0));
+
+               break;
+
+       case '4':
+               if (string_to_number(optarg, 0, 32, &bits))
+                       exit_error(PARAMETER_PROBLEM, 
+                                 "Invalid netmask `%s' specified", optarg);
+               
+               if (bits != 0)
+                       mydata->netmask = 0xFFFFFFFF << (32 - bits);
+
+               *flags |= OPT_CREATE_NETMASK;
+
+               DP("--netmask %x", mydata->netmask);
+               
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to, or --network\n");
+
+       if (flags & OPT_CREATE_NETWORK) {
+               /* --network */
+               if ((flags & OPT_CREATE_FROM) || (flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --from or --to with --network\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+       DP("from : %x to: %x  diff: %d", mydata->from, mydata->to,
+          mydata->to - mydata->from);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n", MAX_RANGE);
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range to large. Max is %d IPs in range\n",
+                          MAX_RANGE);
+       if (flags & OPT_CREATE_NETMASK) {
+               unsigned int mask_bits, netmask_bits;
+               ip_set_ip_t mask;
+               
+               if ((mydata->from & mydata->netmask) != mydata->from)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%s is not a network address according to netmask %d\n",
+                                  ip_tostring(mydata->from, 0),
+                                  mask_to_bits(mydata->netmask));
+               
+               mask = range_to_mask(mydata->from, mydata->to, &mask_bits);
+               if (!mask)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%s-%s is not a full network\n",
+                                  ip_tostring(mydata->from, 0),
+                                  ip_tostring(mydata->to, 0));
+
+               netmask_bits = mask_to_bits(mydata->netmask);
+               
+               if (netmask_bits <= mask_bits)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%d netmask specifies larger or equal netblock than %s-%s\n",
+                                  netmask_bits,
+                                  ip_tostring(mydata->from, 0),
+                                  ip_tostring(mydata->to, 0));
+       }
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {"network", 1, 0, '3'},
+       {"netmask", 1, 0, '4'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(int cmd, const char *optarg,
+                      void *data, const void *setdata)
+{
+       struct ip_set_req_ipmap *mydata =
+           (struct ip_set_req_ipmap *) data;
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) setdata;
+
+       DP("ipmap: %p %p", data, setdata);
+
+       parse_ip(optarg, &mydata->ip);
+
+       DP("from %s", ip_tostring(mysetdata->first_ip, 0));
+       DP("to   %s", ip_tostring(mysetdata->last_ip, 0));
+       DP("ip   %s", ip_tostring(mydata->ip, 0));
+
+       if (mydata->ip < mysetdata->first_ip ||
+           mydata->ip > mysetdata->last_ip)
+               exit_error(PARAMETER_PROBLEM, "IP '%s' is out of range\n",
+                          ip_tostring(mydata->ip, 0));
+
+       return mydata->ip;      
+}
+
+ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) setdata;
+
+       return (mysetdata->first_ip + id * mysetdata->hosts);
+}
+
+ip_set_ip_t sizeid(const void *setdata)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) setdata;
+
+       return (mysetdata->sizeid);
+}
+
+void initheader(void **setdata, void *data, size_t len)
+{
+       struct ip_set_req_ipmap_create *header =
+           (struct ip_set_req_ipmap_create *) data;
+       struct ip_set_ipmap *map;
+               
+       DP("ipmap: initheader() 1");
+
+       if (len != sizeof(struct ip_set_req_ipmap_create))
+               exit_error(OTHER_PROBLEM,
+                          "Ipmap: incorrect size of header. "
+                          "Got %d, wanted %d.", len,
+                          sizeof(struct ip_set_req_ipmap_create));
+
+       *setdata = ipset_malloc(sizeof(struct ip_set_ipmap));
+
+       DP("ipmap: initheader() 2");
+       
+       map = (struct ip_set_ipmap *) *setdata;
+       
+       map->first_ip = header->from;
+       map->last_ip = header->to;
+       map->netmask = header->netmask;
+
+       if (map->netmask == 0xFFFFFFFF) {
+               map->hosts = 1;
+               map->sizeid = map->last_ip - map->first_ip + 1;
+       } else {
+               unsigned int mask_bits, netmask_bits;
+               ip_set_ip_t mask;
+       
+               mask = range_to_mask(header->from, header->to, &mask_bits);
+               netmask_bits = mask_to_bits(header->netmask);
+
+               DP("bits: %i %i", mask_bits, netmask_bits);
+               map->hosts = 2 << (32 - netmask_bits - 1);
+               map->sizeid = 2 << (netmask_bits - mask_bits - 1);
+       }
+
+       DP("%i %i", map->hosts, map->sizeid );
+
+}
+
+void initmembers(void *setdata, void *data, size_t len)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) setdata;
+       size_t size;
+
+       DP("ipmap: initmembers()");
+
+       /* Check so we get the right amount of memberdata */
+       size = bitmap_bytes(0, mysetdata->sizeid - 1);
+
+       if (len != size)
+               exit_error(OTHER_PROBLEM,
+                          "Ipmap: incorrect size of members. "
+                          "Got %d, wanted %d.", len, size);
+
+       mysetdata->members = data;
+}
+
+void killmembers(void **setdata)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) *setdata;
+
+       DP("ipmap: killmembers()");
+
+       if (mysetdata->members != NULL)
+               ipset_free(&mysetdata->members);
+               
+       ipset_free(setdata);
+}
+
+void printheader(const void *setdata, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) setdata;
+
+       printf(" from: %s", ip_tostring(mysetdata->first_ip, options));
+       printf(" to: %s", ip_tostring(mysetdata->last_ip, options));
+       if (mysetdata->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask));
+}
+
+void printips_sorted(const void *setdata, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) setdata;
+       ip_set_ip_t id;
+
+       for (id = 0; id < mysetdata->sizeid; id++)
+               if (test_bit(id, mysetdata->members))
+                       printf("%s\n", ip_tostring(mysetdata->first_ip + id * mysetdata->hosts,
+                                                  options));
+}
+
+void saveheader(const void *setdata)
+{
+       return;
+}
+
+/* Print save for an IP */
+void saveips(const void *setdata)
+{
+       return;
+}
+
+void usage(void)
+{
+       printf
+           ("-N set ipmap --from IP --to IP [--netmask CIDR-netmask]\n"
+            "-N set ipmap --network IP/mask [--netmask CIDR-netmask]\n"
+            "-A set IP\n"
+            "-D set IP\n"
+            "-T set IP\n");
+}
+
+static struct settype settype_ipmap = {
+       .typename = SETTYPE_NAME,
+       .typecode = IPSET_TYPE_IP,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_ipmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .req_size = sizeof(struct ip_set_req_ipmap),
+       .adt_parser = &adt_parser,
+
+       /* Get an IP address by id */
+       .getipbyid = &getipbyid,
+       .sizeid = &sizeid,
+
+       /* Printing */
+       .initheader = &initheader,
+       .initmembers = &initmembers,
+       .killmembers = &killmembers,
+       .printheader = &printheader,
+       .printips = &printips_sorted,   /* We only have sorted version */
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       .usage = &usage,
+       .hint = NULL,
+};
+
+void _init(void)
+{
+       settype_register(&settype_ipmap);
+
+}
diff --git a/ipset_macipmap.c b/ipset_macipmap.c
new file mode 100644 (file)
index 0000000..eff42ce
--- /dev/null
@@ -0,0 +1,387 @@
+/* Copyright 2000, 2001, 2002 Joakim Axelsson (gozem@linux.nu)
+ *                            Patrick Schaaf (bof@bof.de)
+ *                            Martin Josefsson (gandalf@wlug.westbo.se)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+#include <linux/if_ether.h>
+
+#include <linux/netfilter_ipv4/ip_set_macipmap.h>
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+#define OPT_CREATE_NETWORK 0x04U
+#define OPT_CREATE_MATCHUNSET  0x08U
+
+#define OPT_ADDDEL_IP      0x01U
+#define OPT_ADDDEL_MAC     0x02U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       DP("create INIT");
+       /* Nothing */
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_macipmap_create *mydata =
+           (struct ip_set_req_macipmap_create *) data;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_ip(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  ip_tostring(mydata->from, 0));
+
+               break;
+
+       case '2':
+               parse_ip(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to, ip_tostring(mydata->to, 0));
+
+               break;
+
+       case '3':
+               parse_ipandmask(optarg, &mydata->from, &mydata->to);
+
+               /* Make to the last of from + mask */
+               mydata->to = mydata->from | (~mydata->to);
+
+               *flags |= OPT_CREATE_NETWORK;
+
+               DP("--network from %x (%s)", mydata->from,
+                  ip_tostring(mydata->from, 0));
+               DP("--network to   %x (%s)", mydata->to,
+                  ip_tostring(mydata->to, 0));
+
+               break;
+
+       case '4':
+               mydata->flags |= IPSET_MACIP_MATCHUNSET;
+
+               *flags |= OPT_CREATE_MATCHUNSET;
+
+               DP("--matchunset");
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_macipmap_create *mydata =
+           (struct ip_set_req_macipmap_create *) data;
+
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to, or --network\n");
+
+       if (flags & OPT_CREATE_NETWORK) {
+               /* --network */
+               if ((flags & OPT_CREATE_FROM) || (flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --from or --to with --network\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+
+       DP("from : %x to: %x  diff: %d  match unset: %d", mydata->from,
+          mydata->to, mydata->to - mydata->from,
+          flags & OPT_CREATE_MATCHUNSET);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n", MAX_RANGE);
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range to large. Max is %d IPs in range\n",
+                          MAX_RANGE);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {"network", 1, 0, '3'},
+       {"matchunset", 0, 0, '4'},
+       {0}
+};
+
+static void parse_mac(const char *mac, unsigned char *ethernet)
+{
+       unsigned int i = 0;
+
+       if (strlen(mac) != ETH_ALEN * 3 - 1)
+               exit_error(PARAMETER_PROBLEM, "Bad mac address `%s'", mac);
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               long number;
+               char *end;
+
+               number = strtol(mac + i * 3, &end, 16);
+
+               if (end == mac + i * 3 + 2 && number >= 0 && number <= 255)
+                       ethernet[i] = number;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad mac address `%s'", mac);
+       }
+}
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(int cmd, const char *optarg,
+                      void *data, const void *setdata)
+{
+       struct ip_set_req_macipmap *mydata =
+           (struct ip_set_req_macipmap *) data;
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) setdata;
+       char *saved = strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       DP("macipmap: %p %p", data, setdata);
+
+       ptr = strsep(&tmp, "%");
+       parse_ip(ptr, &mydata->ip);
+
+       if (cmd == ADT_ADD && !tmp)
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify ip[mac]\n");
+
+       if (tmp)
+               parse_mac(tmp, mydata->ethernet);
+       else
+               memset(mydata->ethernet, 0, ETH_ALEN);  
+
+       DP("from %s", ip_tostring(mysetdata->first_ip, 0));
+       DP("to   %s", ip_tostring(mysetdata->last_ip, 0));
+       DP("ip   %s", ip_tostring(mydata->ip, 0));
+
+       if (mydata->ip < mysetdata->first_ip ||
+           mydata->ip > mysetdata->last_ip)
+               exit_error(PARAMETER_PROBLEM, "IP '%s' is out of range\n",
+                          ip_tostring(mydata->ip, 0));
+
+       free(saved);
+
+       return mydata->ip;      
+}
+
+ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) setdata;
+
+       return (mysetdata->first_ip + id);
+}
+
+ip_set_ip_t sizeid(const void *setdata)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) setdata;
+
+       return (mysetdata->last_ip - mysetdata->first_ip + 1);
+}
+
+void initheader(void **setdata, void *data, size_t len)
+{
+       struct ip_set_req_macipmap_create *header =
+           (struct ip_set_req_macipmap_create *) data;
+
+       DP("macipmap: initheader() 1");
+
+       if (len != sizeof(struct ip_set_req_macipmap_create))
+               exit_error(OTHER_PROBLEM,
+                          "Macipmap: incorrect size of header. "
+                          "Got %d, wanted %d.", len,
+                          sizeof(struct ip_set_req_macipmap_create));
+
+       *setdata = ipset_malloc(sizeof(struct ip_set_macipmap));
+
+       DP("bitmap: initheader() 2");
+
+       ((struct ip_set_macipmap *) *setdata)->first_ip = header->from;
+       ((struct ip_set_macipmap *) *setdata)->last_ip = header->to;
+       ((struct ip_set_macipmap *) *setdata)->flags = header->flags;
+}
+
+void initmembers(void *setdata, void *data, size_t len)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) setdata;
+       size_t size;
+
+       DP("macipmap: initmembers()");
+
+       /* Check so we get the right amount of memberdata */
+       size = (mysetdata->last_ip - mysetdata->first_ip) 
+               * sizeof(struct ip_set_macip);
+
+       if (len != size)
+               exit_error(OTHER_PROBLEM,
+                          "Macipmap: incorrect size of members. "
+                          "Got %d, wanted %d.", len, size);
+
+       mysetdata->members = data;
+}
+
+void killmembers(void **setdata)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) *setdata;
+
+       DP("macipmap: killmembers()");
+
+       if (mysetdata->members != NULL)
+               ipset_free(&mysetdata->members);
+       
+       ipset_free(setdata);
+}
+
+void printheader(const void *setdata, unsigned options)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) setdata;
+
+       printf(" from: %s", ip_tostring(mysetdata->first_ip, options));
+       printf(" to: %s", ip_tostring(mysetdata->last_ip, options));
+
+       if (mysetdata->flags & IPSET_MACIP_MATCHUNSET)
+               printf(" matchunset");
+       printf("\n");
+}
+
+static void print_mac(unsigned char macaddress[ETH_ALEN])
+{
+       unsigned int i;
+
+       printf("%02X", macaddress[0]);
+       for (i = 1; i < ETH_ALEN; i++)
+               printf(":%02X", macaddress[i]);
+}
+
+void printips_sorted(const void *setdata, unsigned options)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) setdata;
+       struct ip_set_macip *table =
+           (struct ip_set_macip *) mysetdata->members;
+       u_int32_t addr = mysetdata->first_ip;
+
+       while (addr <= mysetdata->last_ip) {
+               if (test_bit(IPSET_MACIP_ISSET,
+                            &table[addr - mysetdata->first_ip].flags)) {
+                       printf("%s%s", ip_tostring(addr, options), "%");
+                       print_mac(table[addr - mysetdata->first_ip].
+                                 ethernet);
+                       printf("\n");
+               }
+               addr++;
+       }
+}
+
+void saveheader(const void *setdata)
+{
+       return;
+}
+
+/* Print save for an IP */
+void saveips(const void *setdata)
+{
+       return;
+}
+
+void usage(void)
+{
+       printf
+           ("-N set macipmap --from IP --to IP [--matchunset]\n"
+            "-N set macipmap --network IP/mask [--matchunset]\n"
+            "-A set IP%%MAC\n"
+            "-D set IP[%%MAC]\n"
+            "-T set IP[%%MAC]\n");
+}
+
+static struct settype settype_macipmap = {
+       .typename = SETTYPE_NAME,
+       .typecode = IPSET_TYPE_IP,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_macipmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .req_size = sizeof(struct ip_set_req_macipmap),
+       .adt_parser = &adt_parser,
+
+       /* Get an IP address by id */
+       .getipbyid = &getipbyid,
+       .sizeid = &sizeid,
+
+       /* Printing */
+       .initheader = &initheader,
+       .initmembers = &initmembers,
+       .killmembers = &killmembers,
+       .printheader = &printheader,
+       .printips = &printips_sorted,   /* We only have sorted version */
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       .usage = &usage,
+       .hint = NULL,
+};
+
+void _init(void)
+{
+       settype_register(&settype_macipmap);
+
+}
diff --git a/ipset_portmap.c b/ipset_portmap.c
new file mode 100644 (file)
index 0000000..fd8746f
--- /dev/null
@@ -0,0 +1,293 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+
+#include <linux/netfilter_ipv4/ip_set_portmap.h>
+#include "ipset.h"
+
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+
+#define OPT_ADDDEL_PORT      0x01U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       DP("create INIT");
+       /* Nothing */
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_portmap_create *mydata =
+           (struct ip_set_req_portmap_create *) data;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_port(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  port_tostring(mydata->from, 0));
+
+               break;
+
+       case '2':
+               parse_port(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  port_tostring(mydata->to, 0));
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_portmap_create *mydata =
+           (struct ip_set_req_portmap_create *) data;
+
+       if (flags == 0) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+       DP("from : %x to: %x  diff: %d", mydata->from, mydata->to,
+          mydata->to - mydata->from);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n", MAX_RANGE);
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range to large. Max is %d ports in range\n",
+                          MAX_RANGE);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(int cmd, const char *optarg,
+                      void *data, const void *setdata)
+{
+       struct ip_set_req_portmap *mydata =
+           (struct ip_set_req_portmap *) data;
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) setdata;
+
+       DP("portmap: %p %p", data, setdata);
+
+       parse_port(optarg, &mydata->port);
+
+       DP("from %s", port_tostring(mysetdata->first_port, 0));
+       DP("to   %s", port_tostring(mysetdata->last_port, 0));
+       DP("port %s", port_tostring(mydata->port, 0));
+
+       if (mydata->port < mysetdata->first_port ||
+           mydata->port > mysetdata->last_port)
+               exit_error(PARAMETER_PROBLEM, "Port '%s' is out of range\n",
+                          port_tostring(mydata->port, 0));
+
+       return mydata->port;    
+}
+
+ip_set_ip_t getportbyid(const void *setdata, ip_set_ip_t id)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) setdata;
+
+       return (mysetdata->first_port + id);
+}
+
+ip_set_ip_t sizeid(const void *setdata)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) setdata;
+
+       return (mysetdata->last_port - mysetdata->first_port + 1);
+}
+
+void initheader(void **setdata, void *data, size_t len)
+{
+       struct ip_set_req_portmap_create *header =
+           (struct ip_set_req_portmap_create *) data;
+
+       DP("portmap: initheader() 1");
+
+       if (len != sizeof(struct ip_set_req_portmap_create))
+               exit_error(OTHER_PROBLEM,
+                          "Portmap: incorrect size of header. "
+                          "Got %d, wanted %d.", len,
+                          sizeof(struct ip_set_req_portmap_create));
+
+       *setdata = ipset_malloc(sizeof(struct ip_set_portmap));
+
+       DP("portmap: initheader() 2");
+
+       ((struct ip_set_portmap *) *setdata)->first_port =
+           header->from;
+       ((struct ip_set_portmap *) *setdata)->last_port = header->to;
+
+}
+
+void initmembers(void *setdata, void *data, size_t len)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) setdata;
+       size_t size;
+
+       DP("portmap: initmembers()");
+
+       /* Check so we get the right amount of memberdata */
+       size = bitmap_bytes(mysetdata->first_port, mysetdata->last_port);
+
+       if (len != size)
+               exit_error(OTHER_PROBLEM,
+                          "Portmap: incorrect size of members. "
+                          "Got %d, wanted %d.", len, size);
+
+       mysetdata->members = data;
+}
+
+void killmembers(void **setdata)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) *setdata;
+
+       DP("portmap: killmembers()");
+
+       if (mysetdata->members != NULL)
+               ipset_free(&mysetdata->members);
+       
+       ipset_free(setdata);
+}
+
+
+void printheader(const void *setdata, unsigned options)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) setdata;
+
+       printf(" from: %s", port_tostring(mysetdata->first_port, options));
+       printf(" to: %s\n", port_tostring(mysetdata->last_port, options));
+}
+
+void printports_sorted(const void *setdata, unsigned options)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) setdata;
+
+       u_int32_t addr = mysetdata->first_port;
+
+       while (addr <= mysetdata->last_port) {
+               if (test_bit(addr - mysetdata->first_port, mysetdata->members))
+                       printf("%s\n", port_tostring(addr, options));
+               addr++;
+       }
+}
+
+void saveheader(const void *setdata)
+{
+       return;
+}
+
+/* Print save for an IP */
+void saveports(const void *setdata)
+{
+       return;
+}
+
+void usage(void)
+{
+       printf
+           ("-N set portmap --from PORT --to PORT\n"
+            "-A set PORT\n"
+            "-D set PORT\n"
+            "-T set PORT\n");
+}
+
+static struct settype settype_portmap = {
+       .typename = SETTYPE_NAME,
+       .typecode = IPSET_TYPE_PORT,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_portmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .req_size = sizeof(struct ip_set_req_portmap),
+       .adt_parser = &adt_parser,
+
+       /* Get an IP address by id */
+       .getipbyid = &getportbyid,
+       .sizeid = &sizeid,
+
+       /* Printing */
+       .initheader = &initheader,
+       .initmembers = &initmembers,
+       .killmembers = &killmembers,
+       .printheader = &printheader,
+       .printips = &printports_sorted, /* We only have sorted version */
+       .printips_sorted = &printports_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveports,
+       .usage = &usage,
+       .hint = NULL,
+};
+
+void _init(void)
+{
+       settype_register(&settype_portmap);
+
+}
diff --git a/libipt_set.h b/libipt_set.h
new file mode 100644 (file)
index 0000000..8180f7c
--- /dev/null
@@ -0,0 +1,132 @@
+#ifndef _LIBIPT_SET_H
+#define _LIBIPT_SET_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+static int get_set_getsockopt(void *data, size_t * size)
+{
+       int sockfd = -1;
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               exit_error(OTHER_PROBLEM,
+                          "Can't open socket to ipset.\n");
+       /* Send! */
+       return getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+}
+
+static void get_set_byname(const char *setname, struct ipt_set_info *info)
+{
+       struct ip_set_req_get req;
+       int size = sizeof(struct ip_set_req_get);
+       int res;
+
+       req.op = IP_SET_OP_GETSET_BYNAME;
+       strncpy(req.name, setname, IP_SET_MAXNAMELEN);
+       req.name[IP_SET_MAXNAMELEN - 1] = '\0';
+       res = get_set_getsockopt(&req, &size);
+       if (res != 0)
+               exit_error(OTHER_PROBLEM,
+                          "Problem when communicating with ipset. errno=%d.\n",
+                          errno);
+       if (size != sizeof(struct ip_set_req_get))
+               exit_error(OTHER_PROBLEM,
+                          "Incorrect return size from kernel during ipset lookup, "
+                          "(want %d, got %d)\n",
+                          sizeof(struct ip_set_req_get), size);
+       if (req.id < 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set %s doesn't exist.\n", setname);
+
+       info->id = req.id;
+}
+
+static void get_set_byid(char * setname, unsigned id)
+{
+       struct ip_set_req_get req;
+       int size = sizeof(struct ip_set_req_get);
+       int res;
+
+       req.op = IP_SET_OP_GETSET_BYID;
+       req.id = id;
+       res = get_set_getsockopt(&req, &size);
+       if (res != 0)
+               exit_error(OTHER_PROBLEM,
+                          "Problem when communicating with ipset. errno=%d.\n",
+                          errno);
+       if (size != sizeof(struct ip_set_req_get))
+               exit_error(OTHER_PROBLEM,
+                          "Incorrect return size from kernel during ipset lookup, "
+                          "(want %d, got %d)\n",
+                          sizeof(struct ip_set_req_get), size);
+       if (req.id < 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set id %i in kernel doesn't exist.\n", id);
+
+       strncpy(setname, req.name, IP_SET_MAXNAMELEN);
+}
+
+static void
+parse_pool(const char *optarg, struct ipt_set_info *info)
+{
+       char *saved = strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       ptr = strsep(&tmp, ":");
+       get_set_byname(ptr, info);
+       
+       while (info->set_level < IP_SET_SETIP_LEVELS && tmp) {
+               ptr = strsep(&tmp, ",");
+               if (strncmp(ptr, "src", 3) == 0)
+                       info->flags[info->set_level++] |= IPSET_SRC;
+               else if (strncmp(ptr, "dst", 3) == 0)
+                       info->flags[info->set_level++] |= IPSET_DST;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "You must spefify (the comma separated list of) 'src' or 'dst'.");
+       }
+
+       if (tmp || info->set_level >= IP_SET_SETIP_LEVELS)
+               exit_error(PARAMETER_PROBLEM,
+                          "Defined childset level is deeper that %i.", 
+                          IP_SET_SETIP_LEVELS);
+
+       free(saved);
+}
+
+static int
+parse_ipflags(const char *optarg, struct ipt_set_info *info)
+{
+       char *saved = strdup(optarg);
+       char *ptr, *tmp = saved;
+       int overwrite = 0;
+
+       info->ip_level = info->set_level;
+       
+       while (info->ip_level < IP_SET_LEVELS && tmp) {
+               if (*tmp == '+') {
+                       info->flags[info->ip_level] |= IPSET_ADD_OVERWRITE;
+                       tmp++;
+                       overwrite++;
+               }
+               ptr = strsep(&tmp, ",");
+               if (strncmp(ptr, "src", 3) == 0)
+                       info->flags[info->ip_level++] |= IPSET_SRC;
+               else if (strncmp(ptr, "dst", 3) == 0)
+                       info->flags[info->ip_level++] |= IPSET_DST;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "You must spefify (the comma separated list of) 'src' or 'dst'.");
+       }
+
+       if (tmp || info->ip_level >= IP_SET_LEVELS)
+               exit_error(PARAMETER_PROBLEM,
+                          "Defined level is deeper that %i.", 
+                          IP_SET_LEVELS);
+                          
+       free(saved);
+       return overwrite;
+}
+
+#endif /*_LIBIPT_SET_H*/