]> granicus.if.org Git - ipset/commitdiff
ipset 2.0 committed
author/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kadlec/emailAddress=kadlec@netfilter.org </C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kadlec/emailAddress=kadlec@netfilter.org>
Wed, 1 Dec 2004 09:07:34 +0000 (09:07 +0000)
committer/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kadlec/emailAddress=kadlec@netfilter.org </C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kadlec/emailAddress=kadlec@netfilter.org>
Wed, 1 Dec 2004 09:07:34 +0000 (09:07 +0000)
ChangeLog
Makefile
ipset.8
ipset.c
ipset.h
ipset_iphash.c
ipset_ipmap.c
ipset_macipmap.c
ipset_portmap.c
libipt_set.h

index c6e20fab6ebb242ea6c5d3d7dc963cc4f877dc1f..4ed34ff932929193f42616f09b773a2fa3defab9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,13 @@
+2.0
+ - Chaining of sets are changed: child sets replaced by bindings
+ - Kernel-userspace communication reorganized to minimize the number
+   of syscalls
+ - Save and restore functionality implemented
+ - iphash type reworked: clashing resolved by double-hashing and by
+   dynamically growing the set
 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)
+ - Renamed to ipset
+ - Rewritten to support child pools
+ - portmap, iphash pool support added
+ - too much other mods here and there to list...
 
index 23302b0c6b17d5c1b9eac71c24f862c8f8b0e719..bba69308bc98bc084a94447a97a8453b26ddf102 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,48 +1,64 @@
 #!/usr/bin/make
 
-IPSET_VERSION:=v1.0
+######################################################################
+# YOU SHOULD NOT NEED TO TOUCH ANYTHING BELOW THIS LINE
+######################################################################
+
+ifndef KERNEL_DIR
+KERNEL_DIR=/usr/src/linux
+endif
+
+IPSET_VERSION:=2.0
+
+PREFIX:=/usr/local
+LIBDIR:=$(PREFIX)/lib
+BINDIR:=$(PREFIX)/sbin
+MANDIR:=$(PREFIX)/man
+INCDIR:=$(PREFIX)/include
 IPSET_LIB_DIR:=$(DESTDIR)$(LIBDIR)/ipset
-#IPSET_LIB_DIR:=.
-#CFLAGS:=-I$(KERNEL_DIR)/include
+
+# directory for new iptables releases
+RELEASE_DIR:=/tmp
+
+COPT_FLAGS:=-O2
+CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -I$(KERNEL_DIR)/include -I. -g -DIPSET_DEBUG #-pg # -DIPTC_DEBUG
 
 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)
+PROGRAMS=ipset
+SHARED_LIBS=$(foreach T, $(SETTYPES),libipset_$(T).so)
+INSTALL=$(DESTDIR)$(BINDIR)/ipset $(DESTDIR)$(MANDIR)/man8/ipset.8
+INSTALL+=$(foreach T, $(SETTYPES), $(DESTDIR)$(LIBDIR)/ipset/libipset_$(T).so)
 
-ifndef TOPLEVEL_INCLUDED
-local:
-       cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS)
+all: $(PROGRAMS) $(SHARED_LIBS)
 
-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)\"
+install: all $(INSTALL)
+
+clean: $(EXTRA_CLEANS)
+       rm -rf $(PROGRAMS) $(SHARED_LIBS) *.o
 
 #The ipset(8) self
-ipset/ipset.o: ipset/ipset.c
-       $(CC) $(CFLAGS) -g -c -o $@ $<
+ipset.o: ipset.c
+       $(CC) $(CFLAGS) -DIPSET_VERSION=\"$(IPSET_VERSION)\" -DIPSET_LIB_DIR=\"$(IPSET_LIB_DIR)\" -c -o $@ $<
 
-ipset/ipset: ipset/ipset.o
+ipsetipset.o
        $(CC) $(CFLAGS) -ldl -rdynamic -o $@ $^
 
 #Pooltypes
-ipset/ipset_%.o: ipset/ipset_%.c
+ipset_%.o: ipset_%.c
        $(CC) $(CFLAGS) -c -o $@ $<
 
-ipset/libipset_%.so: ipset/ipset_%.o
+libipset_%.so: ipset_%.o
        $(LD) -shared -o $@ $<
 
-$(DESTDIR)$(LIBDIR)/ipset/libipset_%.so: ipset/libipset_%.so
+$(DESTDIR)$(LIBDIR)/ipset/libipset_%.so: libipset_%.so
        @[ -d $(DESTDIR)$(LIBDIR)/ipset ] || mkdir -p $(DESTDIR)$(LIBDIR)/ipset
        cp $< $@
 
-$(DESTDIR)$(BINDIR)/ipset: ipset/ipset
+$(DESTDIR)$(BINDIR)/ipset: ipset
        @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
        cp $< $@
 
-$(DESTDIR)$(MANDIR)/man8/ipset.8: ipset/ipset.8
+$(DESTDIR)$(MANDIR)/man8/ipset.8: ipset.8
        @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
        cp $< $@
-endif
diff --git a/ipset.8 b/ipset.8
index 721bc43398e57ac46fba21d8417bc34a2cd1a884..f2c2f0208cb92f76b93c0315ed328b04463081d9 100644 (file)
--- a/ipset.8
+++ b/ipset.8
@@ -18,7 +18,7 @@
 .\"
 .\"
 .SH NAME
-ipset \- IP set administration
+ipset \- administration tool for IP sets
 .SH SYNOPSIS
 .BR "ipset -N " "set type-specification [options]"
 .br
@@ -26,27 +26,30 @@ ipset \- IP set administration
 .br
 .BR "ipset -[EW] " "from-set to-set"
 .br
-.BR "ipset -[ADT] " "set entry"
+.BR "ipset -[ADU] " "set entry"
+.br
+.BR "ipset -B " "set entry -b binding"
+.br
+.BR "ipset -T " "set entry [-b binding]"
 .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.
+.B ipset
+is used to set up, maintain and inspect so called IP sets in the Linux
+kernel. Depending on the type, an IP set may store IP addresses, (TCP/UDP)
+port numbers or additional informations besides IP addresses: the word IP 
+means a general term here. See the set type definitions below.
 .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.
+Any entry in a set can be bound to another set, which forms a relationship
+between a set element and the set it is bound to. The sets may have a 
+default binding, which is valid for every set element for which there is
+no binding defined at all. There is no need for the entry to be
+added to the set for a binding to be defined for it.
 .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'.
+IP set bindings pointing to sets and iptables matches and targets 
+referring to sets creates references, which protects the given sets in 
+the kernel. A set cannot be removed (destroyed) while there is a reference 
+pointing to it.
 .SH OPTIONS
 The options that are recognized by
 .B ipset
@@ -59,104 +62,135 @@ 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.
+.BI "-N, --create " "\fIsetname\fP type type-options"
+Create a set identified with setname and specified type. 
+Type-specific options must be supplied.
+.TP
+.BI "-X, --destroy " "[\fIsetname\fP]"
+Destroy the specified set, or all sets if none or the keyword
+.B
+:all:
+is specified.
+Before destroying the set, all bindings belonging to the 
+set elements and the default binding of the set are removed.
+
+If the set has got references, nothing is done.
+.TP
+.BI "-F, --flush " "[\fIsetname\fP]"
+Delete all entries from the specified set, or flush
+all sets if none or the keyword
+.B
+:all:
+is given. Bindings are not affected by the flush operation.
 .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
+Swap two sets as they referenced in the Linux kernel.
+.B
+iptables
+rules or
+.B
+ipset
+bindings pointing to from-setname will point to to-setname
+and vice versa. Both sets must exist.
+.TP
+.BI "-L, --list " "[\fIsetname\fP]"
+List the entries and bindings for the specified set, or for
+all sets if none or the keyword
+.B
+:all:
+is given. 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.
+option is given, the entries are listed sorted (if the given set
+supports it).
 .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.
+.BI "-S, --save " "[\fIsetname\fP]"
+Save the given set, or all sets if none or the keyword
+.B
+:all:
+is 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.
+Restore a saved session generated by --save. The saved session
+is read from stdin which is required to be rewindable.
+.TP
+.BI "-A, --add " "\fIsetname\fP \fIIP\fP"
+Add an IP to a set.
+.TP
+.BI "-D, --del " "\fIsetname\fP \fIIP\fP"
+Delete an IP from a set. 
+.TP
+.BI "-T, --test " "\fIsetname\fP \fIIP
+Test wether an IP is in a set or not. Exit status number is zero
+if the tested IP is in the set and nonzero if it is missing from 
+the set.
+.TP
+.BI "-T, --test " "\fIsetname\fP \fIIP\fP \fI--binding\fP \fIto-setname\fP"
+Test wether the IP belonging to the set points to the specified binding. 
+Exit status number is zero if the binding points to the specified set, 
+otherwise it is nonzero. The keyword
+.B
+:default:
+can be used to test the default binding of the set.
+.TP
+.BI "-B, --bind " "\fIsetname\fP \fIIP\fP \fI--binding\fP \fIto-setname\fP"
+Bind the IP in setname to to-setname.
+.TP
+.BI "-U, --unbind " "\fIsetname\fP \fIIP\fP"
+Delete the binding belonging to IP in set setname. 
+.TP
+.BI "-H, --help " "[settype]"
+Print help and settype specific help if settype specified.
 .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.
+At the
+.B
+-B, -U
+and
+.B 
+-T
+commands you can use the token
+.B
+:default:
+to bind, unbind or test the default binding of a set instead
+of an IP. At the
+.B
+-U
+command you can use the token
+.B
+:all:
+to destroy the bindings of all elements of a set.
 .SS "OTHER OPTIONS"
 The following additional options can be specified:
 .TP
+.B "-b, --binding setname"
+The option specifies the value of the binding for the
+.B "-B"
+binding command, for which it is a mandatory option.
+You can use it in the
+.B "-T"
+test command as well to test bindings.
+.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).
+Numeric output. When listing sets, bindings, 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), which can trigger
+.B
+slow
+DNS 
+lookups.
 .TP
 .B "-q, --quiet"
-Suppress any output to stdout and stderr. Ipset will still return
+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
@@ -191,7 +225,7 @@ 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
+.I IP%MAC.
 When deleting or testing macipmap entries, the
 .I %MAC
 part is not mandatory.
@@ -223,86 +257,40 @@ Options to use when creating an portmap set:
 .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
+The iphash set type uses a hash to store IP addresses.
+In order to avoid clashes in the hash, double-hashing and, as a last
+resort, dynamic growing of the hash performed. 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
+The initial hash size (default 1024)
 .TP
-.BR "--initval " hash-initval
-Create an iphash set with the specified hashsize and initial 
-(random) hash parameter. (The
-.B "--initval"
-parameter is optional.)
+.BR "--probes " probes
+How many times try to resolve clashing at adding an IP to the hash 
+by double-hashing (default 8).
+.TP
+.BR "--resize " percent
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+.B
+probes
+number of double-hashing. 
 .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 NETFILTER MATCH AND TARGET
-The IP set package adds the `set' match and `SET' target to netfilter:
-.SS set
-The match provides the following option:
-.TP
-.BR "--set " "setname[:flag,...] flag[,flag]"
-where flags are
-.BR "src"
-and/or
-.BR "dst" .
-Hence the command
-.nf
- iptables -A FORWARD -m set --set test:src dst
-.fi
-will match packets, for which there is a child-set under the set named
-as test at the source address or port (depending on the type of the set)
-of the packet, and the destination address or port of the 
-packet (depending of the type of the child set) is set in the child set.
-.SS SET
-The target provides the following option:
-.TP
-.BR "--add-set " "setname[:flag,...] flag[,flag]"
-add the address(es)/port(s) of the packet to the (child)set
-.TP
-.BR "--del-set " "setname[:flag,...] flag[,flag]"
-delete the address(es)/port(s) of the packet from the (child)set,
-where flags are
-.BR "src"
-and/or
-.BR "dst" .
-The flags in the second argument can be preceded by an optional `+' sign, 
-which will force overwriting already existing elements in the target set
-when adding elements to a hash type set.
+.SH GENERAL RESTRICTIONS
+Setnames starting with colon (:) cannot be defined. Zero valued set 
+entries cannot be used.
 .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
diff --git a/ipset.c b/ipset.c
index 04d2487c2523c0058b2cf1c5a8bcb7dd9bf9d9e4..6f903da0935f4d680bb6f87443e048e5a51dccc2 100644 (file)
--- a/ipset.c
+++ b/ipset.c
@@ -1,25 +1,16 @@
-/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu)
+/* Copyright 2000-2002 Joakim Axelsson (gozem@linux.nu)
  *                     Patrick Schaaf (bof@bof.de)
- *                     Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ * Copyright 2003-2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
  *
  * This program is free software; you can redistribute it and/or modify   
- * it under the terms of the GNU General Public License 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
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
  */
 
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <time.h>
 #include <sys/socket.h>
 #include <ctype.h>
 #include <stdlib.h>
 
 #include "ipset.h"
 
-/* The list of all sets */
-static struct set *all_sets = NULL;
+char program_name[] = "ipset";
+char program_version[] = IPSET_VERSION;
 
 /* The list of loaded set types */
 static struct settype *all_settypes = NULL;
 
+/* Array of sets */
+struct set **set_list = NULL;
+ip_set_id_t max_sets = 0;
+
 /* 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
+/* Data for restore mode */
+static int restore = 0;
+void *restore_data = NULL;
+struct ip_set_restore *restore_set = NULL;
+size_t restore_offset = 0, restore_size;
+unsigned line = 0;
 
-#ifdef IP_SET_DEBUG
+#ifdef IPSET_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[] = { 
+/* Most of these command parsing functions are borrowed from iptables.c */
+
+static const char cmdflags[] = { ' ',                  /* CMD_NONE */ 
        'N', 'X', 'F', 'E', 'W', 'L', 'S', 'R', 
-       'A', 'D', 'T', 'H', 'V',
+       'A', 'D', 'T', 'B', 'U', '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
+#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_BINDING            0x0010U         /* -b */
+#define NUMBER_OF_OPT 5
 static const char optflags[] =
-    { 'n', 's', 'q', 'z', 'c', 'i' };
+    { 'n', 's', 'q', 'z', 'b' };
 
 static struct option opts_long[] = {
        /* set operations */
@@ -111,14 +84,17 @@ static struct option opts_long[] = {
        {"del",     1, 0, 'D'},
        {"test",    1, 0, 'T'},
        
+       /* binding operations */
+       {"bind",    1, 0, 'B'},
+       {"unbind",  1, 0, 'U'},
+       
        /* free options */
        {"numeric", 0, 0, 'n'},
        {"sorted",  0, 0, 's'},
        {"quiet",   0, 0, 'q'},
-       {"childsets",0, 0, 'c'},
-       {"hint",    0, 0, 'i'},
+       {"binding", 1, 0, 'b'},
 
-#ifdef IP_SET_DEBUG
+#ifdef IPSET_DEBUG
        /* debug (if compiled with it) */
        {"debug",   0, 0, 'z'},
 #endif
@@ -132,11 +108,10 @@ static struct option opts_long[] = {
 };
 
 static char opts_short[] =
-    "-N:X::F::E:W:L::S::RA:D:T:nsqzciVh::H::";
+    "-N:X::F::E:W:L::S::RA:D:T:B:U:nsqzb:Vh::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).
+/* Table of legal combinations of commands and options. If any of the
+ * given commands make an option legal, that option is legal.
  * Key:
  *  +  compulsory
  *  x  illegal
@@ -144,22 +119,27 @@ static char opts_short[] =
  */
 
 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'},
+       /*            -n   -s   -q   -z   -b  */
+        /*CREATE*/  {'x', 'x', ' ', ' ', 'x'},
+        /*DESTROY*/ {'x', 'x', ' ', ' ', 'x'},
+        /*FLUSH*/   {'x', 'x', ' ', ' ', 'x'},
+        /*RENAME*/  {'x', 'x', ' ', ' ', 'x'},
+        /*SWAP*/    {'x', 'x', ' ', ' ', 'x'},
+        /*LIST*/    {' ', ' ', 'x', ' ', 'x'},
+        /*SAVE*/    {'x', 'x', ' ', ' ', 'x'},
+        /*RESTORE*/ {'x', 'x', ' ', ' ', 'x'},
+        /*ADD*/     {'x', 'x', ' ', ' ', 'x'},
+        /*DEL*/     {'x', 'x', ' ', ' ', 'x'},
+        /*TEST*/    {'x', 'x', ' ', ' ', ' '},
+        /*BIND*/    {'x', 'x', ' ', ' ', '+'},
+        /*UNBIND*/  {'x', 'x', ' ', ' ', 'x'},
+        /*HELP*/    {'x', 'x', 'x', ' ', 'x'},
+        /*VERSION*/ {'x', 'x', 'x', ' ', 'x'},
 };
 
+/* Main parser function */
+int parse_commandline(int argc, char *argv[]);
+
 void exit_tryhelp(int status)
 {
        fprintf(stderr,
@@ -178,6 +158,8 @@ void exit_error(enum exittype status, char *msg, ...)
                vfprintf(stderr, msg, args);
                va_end(args);
                fprintf(stderr, "\n");
+               if (line)
+                       fprintf(stderr, "Restore failed at line %u:\n", line);
                if (status == PARAMETER_PROBLEM)
                        exit_tryhelp(status);
                if (status == VERSION_PROBLEM)
@@ -212,18 +194,18 @@ static void generic_opt_check(int command, int options)
        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)))
+               for (j = 1; j <= NUMBER_OF_CMD; j++) {
+                       if (command != j)
                                continue;
 
                        if (!(options & (1 << i))) {
-                               if (commands_v_options[j][i] == '+')
+                               if (commands_v_options[j-1][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')
+                               if (commands_v_options[j-1][i] != 'x')
                                        legal = 1;
                                else if (legal == 0)
                                        legal = -1;
@@ -246,17 +228,164 @@ static char opt2char(int option)
 
 static char cmd2char(int option)
 {
-       const char *ptr;
-       for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+       if (option <= CMD_NONE || option > NUMBER_OF_CMD)
+               return ' '; 
 
-       return *ptr;
+       return cmdflags[option];
+}
+
+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(unsigned cmd, int err)
+{
+       unsigned int i;
+       struct translate_error {
+               int err;
+               unsigned cmd;
+               char *message;
+       } table[] =
+       { /* Generic error codes */
+         { EPERM, 0, "Missing capability" },
+         { EBADF, 0, "Invalid socket option" },
+         { EINVAL, 0, "Size mismatch for expected socket data" },
+         { ENOMEM, 0, "Not enough memory" },
+         { EFAULT, 0, "Failed to copy data" },
+         { EPROTO, 0, "ipset kernel/userspace version mismatch" },
+         { EBADMSG, 0, "Unknown command" },
+         /* Per command error codes */
+         /* Reserved ones for add/del/test to handle internally: 
+          *    EEXIST
+          */
+         { ENOENT, CMD_CREATE, "Unknown set type" },
+         { ENOENT, 0, "Unknown set" },
+         { EAGAIN, 0, "Sets are busy, try again later" },
+         { ERANGE, CMD_CREATE, "No free slot remained to add a new set" },
+         { ERANGE, 0, "IP/port is outside of the set" },
+         { ENOEXEC, CMD_CREATE, "Invalid parameters to create a set" },
+         { ENOEXEC, CMD_SWAP, "Sets with different types cannot be swapped" },
+         { EEXIST, CMD_CREATE, "Set already exists" },
+         { EEXIST, CMD_RENAME, "Set with new name already exists" },
+         { EBUSY, 0, "Set is in use, operation not permitted" },
+         };
+       for (i = 0; i < sizeof(table)/sizeof(struct translate_error); i++) {
+               if ((table[i].cmd == cmd || table[i].cmd == 0)
+                   && table[i].err == err)
+                       exit_error(err == EPROTO ? VERSION_PROBLEM
+                                                : OTHER_PROBLEM, 
+                                  table[i].message);
+       }
+       exit_error(OTHER_PROBLEM, "Error from kernel: %s", strerror(err));
+}
+
+static void kernel_getfrom(unsigned cmd, void *data, size_t * size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+
+       DP("res=%d errno=%d", res, errno);
+
+       if (res != 0)
+               kernel_error(cmd, errno);
+}
+
+static int kernel_sendto_handleerrno(unsigned cmd, unsigned op,
+                                    void *data, size_t size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+
+       DP("res=%d errno=%d", res, errno);
+
+       if (res != 0) {
+               if (errno == EEXIST)
+                       return -1;
+               else
+                       kernel_error(cmd, errno);
+       }
+
+       return 0; /* all ok */
+}
+
+static void kernel_sendto(unsigned cmd, void *data, size_t size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+
+       DP("res=%d errno=%d", res, errno);
+
+       if (res != 0)
+               kernel_error(cmd, errno);
+}
+
+static int kernel_getfrom_handleerrno(unsigned cmd, void *data, size_t * size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+
+       DP("res=%d errno=%d", res, errno);
+
+       if (res != 0) {
+               if (errno == EAGAIN)
+                       return -1;
+               else
+                       kernel_error(cmd, errno);
+       }
+
+       return 0; /* all ok */
+}
+
+static void check_protocolversion(void)
+{
+       struct ip_set_req_version req_version;
+       size_t size = sizeof(struct ip_set_req_version);
+       int sockfd = kernel_getsocket();
+       int res;
+
+       req_version.op = IP_SET_OP_VERSION;
+       res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size);
+
+       if (res != 0) {
+               ipset_printf("I'm of protocol version %u.\n"
+                            "Kernel module is not loaded in, "
+                            "cannot verify kernel version.",
+                            IP_SET_PROTOCOL_VERSION);
+               return;
+       }
+       if (req_version.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.",
+                          req_version.version, IP_SET_PROTOCOL_VERSION);
 }
 
 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));
+                          cmd2char(*cmd), cmd2char(newcmd));
        *cmd = newcmd;
 }
 
@@ -277,7 +406,7 @@ void *ipset_malloc(size_t size)
                return NULL;
 
        if ((p = malloc(size)) == NULL) {
-               perror("ipset: malloc failed");
+               perror("ipset: not enough memory");
                exit(1);
        }
        return p;
@@ -320,8 +449,6 @@ 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) {
@@ -336,11 +463,11 @@ 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)
+                               AF_INET)) != NULL) {
+               DP("%s", net->n_name);
                return (char *) net->n_name;
+       }
 
        return (char *) NULL;
 }
@@ -370,17 +497,13 @@ void parse_ip(const char *str, ip_set_ip_t * ip)
        struct hostent *host;
        struct in_addr addr;
 
+       DP("%s", str);
+       
        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
-
+               if (!*ip)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Zero valued IP address `%s' specified", str);
                return;
        }
 
@@ -397,15 +520,10 @@ void parse_ip(const char *str, ip_set_ip_t * ip)
                                   "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
-
+               if (!*ip)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Zero valued IP address `%s' specified",
+                                  str);
                return;
        }
 
@@ -418,7 +536,7 @@ void parse_mask(const char *str, ip_set_ip_t * mask)
        struct in_addr addr;
        unsigned int bits;
 
-       DP("parse_mask %s", str);
+       DP("%s", str);
 
        if (str == NULL) {
                /* no mask at all defaults to 32 bits */
@@ -460,13 +578,13 @@ parse_ipandmask(const char *str, ip_set_ip_t * ip, ip_set_ip_t * mask)
        else
                parse_ip(buf, ip);
 
-       DP("parse_ipandmask: %s ip: %08X (%s) mask: %08X",
+       DP("%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",
+       DP("%s ip: %08X (%s) mask: %08X",
           str, *ip, ip_tostring(*ip, 0), *mask);
 }
 
@@ -479,10 +597,10 @@ 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;
-       
+       if (!(options & OPT_NUMERIC)) {
+               if ((service = getservbyport(htons(port), "tcp")))
+                       return service->s_name;
+       }
        sprintf(name, "%u", port);
        return name;
 }
@@ -524,15 +642,23 @@ string_to_port(const char *str, ip_set_ip_t *port)
 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);
+             && (string_to_port(str, port) != 0))
+               exit_error(PARAMETER_PROBLEM, 
+                          "Invalid TCP port `%s' specified", str);
+       
+       if (!*port)
+               exit_error(PARAMETER_PROBLEM, 
+                          "Zero valued port `%s' specified", str);
 }
 
+/* 
+ * Settype functions
+ */
 static struct settype *settype_find(const char *typename)
 {
        struct settype *runner = all_settypes;
 
-       DP("settype %s", typename);
+       DP("%s", typename);
 
        while (runner != NULL) {
                if (strncmp(runner->typename, typename, 
@@ -576,1051 +702,1012 @@ static struct settype *settype_load(const char *typename)
        return NULL;            /* Never executed, but keep compilers happy */
 }
 
+static char *check_set_name(char *setname)
+{
+       if (strlen(setname) > IP_SET_MAXNAMELEN - 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Setname '%s' too long, max %d characters.",
+                          setname, IP_SET_MAXNAMELEN - 1);
+
+       return setname;
+}
+
+static struct settype *check_set_typename(const char *typename)
+{
+       if (strlen(typename) > IP_SET_MAXNAMELEN - 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Typename '%s' too long, max %d characters.",
+                          typename, IP_SET_MAXNAMELEN - 1);
+
+       return settype_load(typename);
+}
+
+#define MAX(a,b)       ((a) > (b) ? (a) : (b))
+
 /* Register a new set type */
 void settype_register(struct settype *settype)
 {
        struct settype *chk;
+       size_t size;
 
-       DP("register_settype '%s'\n", settype->typename);
+       DP("%s", 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",
+                          "Set type '%s' already registered!\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,
+                          "Set type %s is of wrong protocol version %u!"
+                          " I'm of version %u.\n", settype->typename,
                           settype->protocol_version,
                           IP_SET_PROTOCOL_VERSION);
 
+       /* Initialize internal data */
+       settype->header = ipset_malloc(settype->header_size);
+       size = MAX(settype->create_size, settype->adt_size);
+       settype->data = ipset_malloc(size);
+
        /* 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);
+       DP("%s registered", settype->typename);
 }
 
-static int kernel_getsocket(void)
+/* Find set functions */
+static struct set *set_find_byid(ip_set_id_t id)
 {
-       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;
+       struct set *set = NULL;
+       ip_set_id_t i;
+       
+       for (i = 0; i < max_sets; i++)
+               if (set_list[i] && set_list[i]->id == id) {
+                       set = set_list[i];
+                       break;
+       }
+                       
+       if (set == NULL)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set identified by id %u is not found", id);
+       return set;
 }
 
-static void kernel_error(int err)
+static struct set *set_find_byname(const char *name)
 {
-       /* translate errnos, as returned by our *sockopt() functions */
-       exit_error(OTHER_PROBLEM, "Error from kernel: %s", strerror(err));
+       struct set *set = NULL;
+       ip_set_id_t i;
+       
+       for (i = 0; i < max_sets; i++)
+               if (set_list[i]
+                   && strncmp(set_list[i]->name, name,
+                              IP_SET_MAXNAMELEN) == 0) {
+                       set = set_list[i];
+                       break;
+               }
+       if (set == NULL)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set %s is not found", name);
+       return set;
 }
 
-static void kernel_sendto(void *data, size_t size)
+static ip_set_id_t set_find_free_index(const char *name)
 {
-       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);
+       ip_set_id_t i, index = IP_SET_INVALID_ID;
+
+       for (i = 0; i < max_sets; i++) {
+               if (index == IP_SET_INVALID_ID
+                   && set_list[i] == NULL)
+                       index = i;
+               if (set_list[i] != NULL
+                   && strncmp(set_list[i]->name, name,
+                              IP_SET_MAXNAMELEN) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Set %s is already defined, cannot be restored",
+                                  name);
+       }
+                       
+       if (index == IP_SET_INVALID_ID)         
+               exit_error(PARAMETER_PROBLEM,
+                          "Set %s cannot be restored, "
+                          "max number of set %u reached",
+                          name, max_sets);
 
-       if (res != 0)
-               kernel_error(errno);
+       return index;
 }
 
-/* Used by addip and delip that has to handle the EEXIST error better */
-static int kernel_sendto_handleexist(void *data, size_t size)
+/* 
+ * Send create set order to kernel
+ */
+static void set_create(const char *name, struct settype *settype)
 {
-       int res;
-       int sockfd = kernel_getsocket();
+       struct ip_set_req_create req_create;
+       size_t size;
+       void *data;
 
-       /* Send! */
-       res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+       DP("%s %s", name, settype->typename);
+
+       req_create.op = IP_SET_OP_CREATE;
+       req_create.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req_create.name, name);
+       strcpy(req_create.typename, settype->typename);
+
+       /* Final checks */
+       settype->create_final(settype->data, settype->flags);
 
-       DP("kernel_sendto_handleexist() res=%d errno=%d\n", res, errno);
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_create) + settype->create_size;
+       data = ipset_malloc(size);
 
-       if (res != 0 && errno == EEXIST)
-               return -1;
-       else if (res != 0)
-               kernel_error(errno);
+       /* 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);
 
-       return 0;               /* all ok */
+       kernel_sendto(CMD_CREATE, data, size);
+       free(data);
 }
 
-static void kernel_getfrom(void *data, size_t * size)
+static void set_restore_create(const char *name, struct settype *settype)
 {
-       int res;
-       int sockfd = kernel_getsocket();
-
-       /* Send! */
-       res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+       struct set *set;
+       
+       DP("%s %s %u %u %u %u", name, settype->typename,
+          restore_offset, sizeof(struct ip_set_restore),
+          settype->create_size, restore_size);
+
+       /* Sanity checking */
+       if (restore_offset
+           + sizeof(struct ip_set_restore)
+           + settype->create_size > restore_size)
+               exit_error(PARAMETER_PROBLEM,
+                          "Giving up, restore file is screwed up!");
+                          
+       /* Final checks */
+       settype->create_final(settype->data, settype->flags);
 
-       DP("kernel_getfrom() res=%d errno=%d\n", res, errno);
+       /* Fill out restore_data */
+       restore_set = (struct ip_set_restore *) 
+                       (restore_data + restore_offset);
+       strcpy(restore_set->name, name);
+       strcpy(restore_set->typename, settype->typename);
+       restore_set->index = set_find_free_index(name);
+       restore_set->header_size = settype->create_size;
+       restore_set->members_size = 0;
+
+       DP("name %s, restore index %u", restore_set->name, restore_set->index);
+       /* Add settype data */
+       
+       memcpy(restore_data + restore_offset + sizeof(struct ip_set_restore),
+              settype->data, settype->create_size);
 
-       if (res != 0)
-               kernel_error(errno);
+       restore_offset += sizeof(struct ip_set_restore)
+                         + settype->create_size;       
+       
+       /* Add set to set_list */
+       set = ipset_malloc(sizeof(struct set));
+       strcpy(set->name, name);
+       set->settype = settype;
+       set->index = restore_set->index;
+       set_list[restore_set->index] = set;
 }
 
-static int get_protocolversion(void)
+/*
+ * Send destroy/flush order to kernel for one or all sets
+ */
+static void set_destroy(const char *name, unsigned op, unsigned cmd)
 {
-       struct ip_set_req_version req_version;
-       size_t size = sizeof(struct ip_set_req_version);
+       struct ip_set_req_std req;
 
-       req_version.op = IP_SET_OP_VERSION;
-
-       kernel_getfrom(&req_version, &size);
+       DP("%s %s", cmd == CMD_DESTROY ? "destroy" : "flush", name);
 
-       DP("get_protocolversion() ver=%d", req_version.version);
-
-       return req_version.version;
-}
+       req.op = op;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req.name, name);
 
-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;
+       kernel_sendto(cmd, &req, sizeof(struct ip_set_req_std));
 }
 
-static void get_sets(void)
+/*
+ * Send rename/swap order to kernel
+ */
+static void set_rename(const char *name, const char *newname,
+                      unsigned op, unsigned cmd)
 {
-       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;
+       struct ip_set_req_create req;
 
-       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("%s %s %s", cmd == CMD_RENAME ? "rename" : "swap",
+                      name, newname);
 
-               DP("got size: %d", size);
+       req.op = op;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req.name, name);
+       strcpy(req.typename, newname);
 
-               if (req_list.size == 0)
-                       return;         /* No sets in kernel */
+       kernel_sendto(cmd, &req,
+                     sizeof(struct ip_set_req_create));
+}
 
-               data = ipset_malloc(size);
-               ((struct ip_set_req_base *) data)->op = IP_SET_OP_LISTING;
+/*
+ * Send MAX_SETS, LIST_SIZE and/or SAVE_SIZE orders to kernel
+ */
+static size_t load_set_list(const char name[IP_SET_MAXNAMELEN],
+                           ip_set_id_t *index,
+                           unsigned op, unsigned cmd)
+{
+       void *data = NULL;
+       struct ip_set_req_max_sets req_max_sets;
+       struct ip_set_name_list *name_list;
+       struct set *set;
+       ip_set_id_t i;
+       size_t size, req_size;
+       int repeated = 0, res = 0;
+
+       DP("%s %s", cmd == CMD_MAX_SETS ? "MAX_SETS"
+                   : cmd == CMD_LIST_SIZE ? "LIST_SIZE"
+                   : "SAVE_SIZE",
+                   name);
+       
+tryagain:
+       if (set_list) {
+               for (i = 0; i < max_sets; i++)
+                       if (set_list[i])
+                               free(set_list[i]);
+               free(set_list);
+               set_list = NULL;
+       }
+       /* Get max_sets */
+       req_max_sets.op = IP_SET_OP_MAX_SETS;
+       req_max_sets.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req_max_sets.set.name, name);
+       size = sizeof(req_max_sets);
+       kernel_getfrom(CMD_MAX_SETS, &req_max_sets, &size);
+
+       DP("got MAX_SETS: sets %d, max_sets %d",
+          req_max_sets.sets, req_max_sets.max_sets);
+
+       max_sets = req_max_sets.max_sets;
+       set_list = ipset_malloc(max_sets * sizeof(struct set *));
+       memset(set_list, 0, max_sets * sizeof(struct set *));
+       *index = req_max_sets.set.index;
+
+       if (req_max_sets.sets == 0)
+               /* No sets in kernel */
+               return 0;
 
-               /* Get! */
-               res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, &size);
-               DP("list_get getsockopt() res=%d errno=%d\n", res, errno);
+       /* Get setnames */
+       size = req_size = sizeof(struct ip_set_req_setnames) 
+                         + req_max_sets.sets * sizeof(struct ip_set_name_list);
+       data = ipset_malloc(size);
+       ((struct ip_set_req_setnames *) data)->op = op;
+       ((struct ip_set_req_setnames *) data)->index = *index;
 
-               if (res == 0)
-                       goto got_sets;  /* all OK */
-               else if (errno != ENOMEM)
-                       break;          /* Not a memory error */
+       res = kernel_getfrom_handleerrno(cmd, data, &size);
 
-               DP("not enough mem, looping again");
+       if (res != 0 || size != req_size) {
                free(data);
-       }
-
-       if (errno == ENOMEM)
+               if (repeated++ < LIST_TRIES)
+                       goto tryagain;
                exit_error(OTHER_PROBLEM,
-                          "Tried to list sets from kernel %d times"
+                          "Tried to get 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:");
+       /* Load in setnames */
+       size = sizeof(struct ip_set_req_setnames);                      
+       while (size + sizeof(struct ip_set_name_list) <= req_size) {
+               name_list = (struct ip_set_name_list *)
+                       (data + size);
+               set = ipset_malloc(sizeof(struct set));
+               strcpy(set->name, name_list->name);
+               set->index = name_list->index;
+               set->settype = settype_load(name_list->typename);
+               set_list[name_list->index] = set;
+               DP("loaded %s, type %s, index %u",
+                  set->name, set->settype->typename, set->index);
+               size += sizeof(struct ip_set_name_list);
+       }
+       /* Size to get set members, bindings */
+       size = ((struct ip_set_req_setnames *)data)->size;
+       free(data);
+       
+       return size;
+}
 
-                       for (i = data + eaten;
-                            i <
-                            data + eaten +
-                            sizeof(struct ip_set_req_listing); i += 4) {
-                               unsigned *j = (unsigned *) i;
-                               DP("%x", *j);
-                       }
-               }
-#endif
+/*
+ * Save operation
+ */
+static size_t save_bindings(void *data, size_t offset, size_t len)
+{
+       struct ip_set_hash_save *hash =
+               (struct ip_set_hash_save *) (data + offset);
+       struct set *set;
+       char * (*printip)(ip_set_ip_t ip, unsigned options);
 
-               /* 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);
-               }
+       DP("offset %u, len %u", offset, len);
+       if (offset + sizeof(struct ip_set_hash_save) > len)
+               exit_error(OTHER_PROBLEM,
+                          "Save operation failed, try again later.");
 
-               eaten += sizeof(struct ip_set_req_listing);
+       set = set_find_byid(hash->id);
+       if (!(set && set_list[hash->binding]))
+               exit_error(OTHER_PROBLEM,
+                          "Save binding failed, try again later.");
+       printip = set->settype->typecode == IPSET_TYPE_IP ?
+                       ip_tostring : port_tostring;
+       printf("-B %s %s -b %s\n",
+               set->name,
+               printip(hash->ip, OPT_NUMERIC),
+               set_list[hash->binding]->name);
+
+       return sizeof(struct ip_set_hash_save);
+}              
+
+static size_t save_set(void *data, int *bindings,
+                      size_t offset, size_t len)
+{
+       struct ip_set_save *set_save =
+               (struct ip_set_save *) (data + offset);
+       struct set *set;
+       struct settype *settype;
+       size_t used;
+       
+       DP("offset %u, len %u", offset, len);
+       if (offset + sizeof(struct ip_set_save) > len
+           || offset + sizeof(struct ip_set_save)
+              + set_save->header_size + set_save->members_size > len)
+               exit_error(OTHER_PROBLEM,
+                          "Save operation failed, try again later.");
 
-               DP("insert in list");
-               set_append(set);
+       if (set_save->index == IP_SET_INVALID_ID) {
+               /* Marker */
+               *bindings = 1;
+               return sizeof(struct ip_set_save);
        }
+       set = set_list[set_save->index];
+       if (!set)
+               exit_error(OTHER_PROBLEM,
+                          "Save set failed, try again later.");
+       settype = set->settype;
 
-       DP("free");
+       /* Init set header */
+       used = sizeof(struct ip_set_save);
+       settype->initheader(set, data + offset + used);
 
-       free(data);
+       /* Print create set */
+       settype->saveheader(set, OPT_NUMERIC);
 
-       DP("get_sets() eaten = %d, size = %d", eaten, size);
+       /* Print add IPs */
+       used += set_save->header_size;
+       settype->saveips(set, data + offset + used,
+                        set_save->members_size, OPT_NUMERIC);
 
-       if (eaten != size)
-               exit_error(OTHER_PROBLEM,
-                          "Desynched in listing of sets from kernel. "
-                          "Read %d bytes. Worked %d bytes.", size, eaten);
+       return (used + set_save->members_size);
 }
 
-struct set *set_find(const char *setname)
+static size_t save_default_bindings(void *data, int *bindings)
 {
-       struct set *set = all_sets;
+       struct ip_set_save *set_save = (struct ip_set_save *) data;
+       struct set *set;
+       
+       if (set_save->index == IP_SET_INVALID_ID) {
+               /* Marker */
+               *bindings = 1;
+               return sizeof(struct ip_set_save);
+       }
 
-       DP("%s", setname);
+       set = set_list[set_save->index];
+       DP("%s, binding %u", set->name, set_save->binding);
+       if (set_save->binding != IP_SET_INVALID_ID) {
+               if (!set_list[set_save->binding])
+                       exit_error(OTHER_PROBLEM,
+                                  "Save set failed, try again later.");
 
-       while (set != NULL) {
-               if (strcmp(setname, set->name) == 0)
-                       return set;
-               set = set->next;
+               printf("-B %s %s -b %s\n",
+                       set->name, IPSET_TOKEN_DEFAULT, 
+                       set_list[set_save->binding]->name);
        }
-       return NULL;
+       return (sizeof(struct ip_set_save)
+               + set_save->header_size
+               + set_save->members_size);
 }
 
-static struct set *set_checkname(const char *setname)
+static int try_save_sets(const char name[IP_SET_MAXNAMELEN])
 {
-       char *saved = strdup(setname);
-       char *ptr, *tmp = saved;
-       struct set *set;
-       int i;
-
-       DP("%s", setname);
-
-       /* Cleanup */
-       if (set_name != NULL)
-               free(set_name);
+       void *data = NULL;
+       size_t size, req_size = 0;
+       ip_set_id_t index;
+       int res = 0, bindings = 0;
+       time_t now = time(NULL);
+
+       /* Load set_list from kernel */
+       size = load_set_list(name, &index,
+                            IP_SET_OP_SAVE_SIZE, CMD_SAVE);
        
-       for (i = 0; i < IP_SET_SETIP_LEVELS; i++)
-               set_ip[i] = 0;
-       set_level = 0;
+       if (size) {
+               /* Get sets, bindings and print them */
+               /* Take into account marker */
+               req_size = (size += sizeof(struct ip_set_save));
+               data = ipset_malloc(size);
+               ((struct ip_set_req_list *) data)->op = IP_SET_OP_SAVE;
+               ((struct ip_set_req_list *) data)->index = index;
+               res = kernel_getfrom_handleerrno(CMD_SAVE, data, &size);
 
-       /* 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);
+               if (res != 0 || size != req_size) {
+                       free(data);
+                       return -EAGAIN;
+               }
        }
 
-       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);
+       printf("# Generated by ipset %s on %s", IPSET_VERSION, ctime(&now));
+       size = 0;
+       while (size < req_size) {
+               DP("size: %u, req_size: %u", size, req_size);
+               if (bindings)
+                       size += save_bindings(data, size, req_size);
+               else
+                       size += save_set(data, &bindings, size, req_size);
        }
-       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);
+       /* Re-read data to save default bindings */
+       bindings = 0;
+       size = 0;
+       while (size < req_size && bindings == 0)
+               size += save_default_bindings(data + size, &bindings);
+
+       printf("COMMIT\n");
+       now = time(NULL);
+       printf("# Completed on %s", ctime(&now));
+       ipset_free(&data);
+       return res;
 }
 
-/* Get setid from kernel. */
-static void
-set_get_setid(struct set *set)
+/*
+ * Performs a save to stdout
+ */
+static void set_save(const char name[IP_SET_MAXNAMELEN])
 {
-       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);
+       int i;
 
-       DP("set_get_setid() result=%u", req_get.id);
+       DP("%s", name);
+       for (i = 0; i < LIST_TRIES; i++)
+               if (try_save_sets(name) == 0)
+                       return;
 
-       if (req_get.id < 0)
+       if (errno == EAGAIN)
                exit_error(OTHER_PROBLEM,
-                          "Set %s cannot be found.",
-                          set->name);
-
-       set->id = req_get.id;
-       set->ref = req_get.ref;
+                          "Tried to save sets from kernel %d times"
+                          " and failed. Please try again when the load on"
+                          " the sets has gone down.", LIST_TRIES);
+       else
+               kernel_error(CMD_SAVE, errno);
 }
 
-/* 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
+/*
+ * Restore operation
+ */
 
-       kernel_sendto(data, size);
-       free(data);
-       
-       /* Get set id from kernel */
-       set_get_setid(set);
-       /* Success! */
-       set_append(set);
-}
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc = 0;
 
-/* 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;
+/* Build faked argv from parsed line */
+static void build_argv(int line, char *buffer) {
+       char *ptr;
        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);
+       /* Reset */     
+       for (i = 1; i < newargc; i++)
+               free(newargv[i]);
+       newargc = 1;
 
-#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);
+       ptr = strtok(buffer, " \t\n");
+       newargv[newargc++] = strdup(ptr);
+       while ((ptr = strtok(NULL, " \t\n")) != NULL) {
+               if ((newargc + 1) < sizeof(newargv)/sizeof(char *))
+                       newargv[newargc++] = strdup(ptr);
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Line %d is too long to restore\n", line);
        }
-               
-       free(set);
 }
 
-/* Sends destroy order to kernel for one or all sets
- * All sets: set == NULL
+/*
+ * Performs a restore from a file
  */
-static void set_destroy(struct set *set)
+static void set_restore(FILE *in, char *argv0)
 {
-       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);
+       char buffer[1024];      
+       char *ptr, *name = NULL;
+       char cmd = ' ';
+       int line = 0, first_pass, i, bindings = 0;
+       struct settype *settype = NULL;
+       struct ip_set_req_setnames *header;
+       ip_set_id_t index;
+       int res;
+       
+       /* Load existing sets from kernel */
+       load_set_list(IPSET_TOKEN_ALL, &index,
+                     IP_SET_OP_LIST_SIZE, CMD_RESTORE);
+       
+       restore_size = sizeof(struct ip_set_req_setnames)/* header */
+                      + sizeof(struct ip_set_restore);  /* marker */
+       DP("restore_size: %u", restore_size);
+       /* First pass: calculate required amount of data */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               line++;
+
+               if (buffer[0] == '\n')
+                       continue;
+               else if (buffer[0] == '#')
+                       continue;
+               else if (strcmp(buffer, "COMMIT\n") == 0) {
+                       /* Enable restore mode */
+                       restore = 1;
+                       break;
                }
-       } 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);
+               /* -N, -A or -B */
+               ptr = strtok(buffer, " \t\n");
+               DP("ptr: %s", ptr);
+               if (ptr == NULL
+                   || ptr[0] != '-'
+                   || !(ptr[1] == 'N'
+                        || ptr[1] == 'A'
+                        || ptr[1] == 'B')
+                   || ptr[2] != '\0') {
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Line %u does not start as a valid restore command\n",
+                                  line);
                }
-       }
-}
-
-/* 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;
+               cmd = ptr[1];           
+               /* setname */
+               ptr = strtok(NULL, " \t\n");
+               DP("setname: %s", ptr);
+               if (ptr == NULL)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Missing set name in line %u\n",
+                                  line);
+               DP("cmd %c", cmd);
+               switch (cmd) {
+               case 'N': {
+                       name = check_set_name(ptr);
+                       /* settype */
+                       ptr = strtok(NULL, " \t\n");
+                       if (ptr == NULL)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Missing settype in line %u\n",
+                                          line);
+                       if (restore)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid line %u: create must precede bindings\n",
+                                          line);
+                       settype = check_set_typename(ptr);
+                       restore_size += sizeof(struct ip_set_restore)
+                                       + settype->create_size;
+                       DP("restore_size (N): %u", restore_size);
+                       break; 
                }
-       } 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));
+               case 'A': {
+                       if (strncmp(name, ptr, sizeof(name)) != 0)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Add IP to set %s in line %u without "
+                                          "preceding corresponding create set line\n",
+                                          ptr, line);
+                       if (restore)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid line %u: adding entries must precede bindings\n",
+                                          line);
+                       restore_size += settype->adt_size;
+                       DP("restore_size (A): %u", restore_size);
+                       break;
+               }
+               case 'B': {
+                       bindings = 1;
+                       restore_size += sizeof(struct ip_set_hash_save);
+                       DP("restore_size (B): %u", restore_size);
+                       break;
+               }
+               default: {
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unrecognized restore command in line %u\n",
+                                  line);
+               }
+               } /* end of switch */
+       }                       
+       /* Sanity checking */
+       if (!restore)
+               exit_error(PARAMETER_PROBLEM,
+                          "Missing COMMIT line\n");
+       DP("restore_size: %u", restore_size);
+       restore_data = ipset_malloc(restore_size);
+       header = (struct ip_set_req_setnames *) restore_data;
+       header->op = IP_SET_OP_RESTORE; 
+       header->size = restore_size; 
+       restore_offset = sizeof(struct ip_set_req_setnames);
+
+       /* Rewind to scan the file again */
+       res = fseek(in, 0L, SEEK_SET);
+       if (res)
+               exit_error(PARAMETER_PROBLEM,
+                          "Cannot rewind stdin: %s", strerror(errno));
+       first_pass = line;
+       line = 0;
        
-       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));
+       /* Initialize newargv/newargc */
+       newargv[newargc++] = strdup(argv0);
        
-       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;
+       /* Second pass: build up restore request */
+       while (fgets(buffer, sizeof(buffer), in)) {             
+               line++;
 
-       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);
+               if (buffer[0] == '\n')
+                       continue;
+               else if (buffer[0] == '#')
+                       continue;
+               else if (strcmp(buffer, "COMMIT\n") == 0)
+                       goto do_restore;
+               DP("restoring: %s", buffer);
+               /* Build faked argv, argc */
+               build_argv(line, buffer);
+               for (i = 0; i < newargc; i++)
+                       DP("argv[%u]: %s", i, newargv[i]);
+               
+               /* Parse line */
+               parse_commandline(newargc, newargv);
+       }
+       exit_error(PARAMETER_PROBLEM,
+                  "Broken restore file\n");
+   do_restore:
+       if (bindings == 0
+           && restore_size == 
+              (restore_offset + sizeof(struct ip_set_restore))) {
+               /* No bindings */
+               struct ip_set_restore *marker = 
+                       (struct ip_set_restore *) (restore_data + restore_offset);
+
+               DP("restore marker");
+               marker->index = IP_SET_INVALID_ID;
+               marker->header_size = marker->members_size = 0;
+               restore_offset += sizeof(struct ip_set_restore);
+       }
+       if (restore_size != restore_offset)
+               exit_error(PARAMETER_PROBLEM,
+                          "Giving up, restore file is screwed up!");
+       res = kernel_getfrom_handleerrno(CMD_RESTORE, restore_data, &restore_size);
 
-       /* All OK, return */
-       return data;
+       if (res != 0) {
+               if (restore_size != sizeof(struct ip_set_req_setnames))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Communication with kernel failed (%u %u)!",
+                                  restore_size, sizeof(struct ip_set_req_setnames));
+               /* Check errors  */
+               header = (struct ip_set_req_setnames *) restore_data;
+               if (header->size != 0) 
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Committing restoring failed at line %u!",
+                                  header->size);
+       }
 }
 
-static void list_getheaders(struct set *set, ip_set_ip_t *ip, unsigned level)
+/*
+ * Send ADT_GET order to kernel for a set
+ */
+static struct set *set_adt_get(const char *name)
 {
-       void *data;
+       struct ip_set_req_adt_get req_adt_get;
+       struct set *set;
        size_t size;
 
-       data = list_get(IP_SET_OP_LIST_HEADER_SIZE,
-                       IP_SET_OP_LIST_HEADER, 
-                       set->id, &size, ip, level);
+       DP("%s", name);
 
-       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);
+       req_adt_get.op = IP_SET_OP_ADT_GET;
+       req_adt_get.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req_adt_get.set.name, name);
+       size = sizeof(struct ip_set_req_adt_get);
 
-       /* Handle the data */
-       set->settype[level]->initheader(&set->private[level].setdata,
-                                       data, size);
+       kernel_getfrom(CMD_ADT_GET, &req_adt_get, &size);
 
-       ipset_free(&data);
-}
-
-static void list_getmembers(struct set *set, ip_set_ip_t *ip, unsigned level)
-{
-       void *data;
-       size_t size;
+       set = ipset_malloc(sizeof(struct set));
+       strcpy(set->name, name);
+       set->index = req_adt_get.set.index;     
+       set->settype = settype_load(req_adt_get.typename);
 
-       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 */
-}
+       return set;
+}      
 
-static void list_getchildsets(struct set *set, ip_set_ip_t *ip, unsigned level)
+/*
+ * Send add/del/test order to kernel for a set
+ */
+static int set_adtip(struct set *set, const char *adt, 
+                    unsigned op, unsigned cmd)
 {
-       void *data;
+       struct ip_set_req_adt *req_adt;
        size_t size;
+       void *data;
+       int res = 0;
 
-       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;
+       DP("%s -> %s", set->name, adt);
 
-       /* Next list_getchildsets or set_del frees the data */
-}
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_adt) + set->settype->adt_size ;
+       DP("alloc size %i", size);
+       data = ipset_malloc(size);
 
-/* 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;
+       /* Fill out the request */
+       req_adt = (struct ip_set_req_adt *) data;
+       req_adt->op = op;
+       req_adt->index = set->index;
+       memcpy(data + sizeof(struct ip_set_req_adt),
+              set->settype->data, set->settype->adt_size);
        
-       /* 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));
+       if (kernel_sendto_handleerrno(cmd, op, data, size) == -1)
+               switch (op) {
+               case IP_SET_OP_ADD_IP:
+                       exit_error(OTHER_PROBLEM, "%s is already in set %s.",
+                                  adt, set->name);
                        break;
-               case IPSET_TYPE_PORT:
-                       printf("%s%s", i == 0 ? ":" : ",",
-                                      port_tostring(ip[i], options));
+               case IP_SET_OP_DEL_IP:
+                       exit_error(OTHER_PROBLEM, "%s is not in set %s.",
+                                  adt, set->name);
+                       break;
+               case IP_SET_OP_TEST_IP:
+                       ipset_printf("%s is in set %s.", adt, set->name);
+                       res = 0;
                        break;
                default:
-                       ;
+                       break;
                }
-       }
-       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);
+               switch (op) {
+               case IP_SET_OP_TEST_IP:
+                       ipset_printf("%s is NOT in set %s.", adt, set->name);
+                       res = 1;
+                       break;
+               default:
+                       break;
                }
-       }
-}
-
-/* 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);
+       free(data);
 
-       printf("\n");           /* One newline between sets */
+       return res;
 }
 
-/* Print a set or all sets
- * All sets: set = NULL
- */
-static void set_list(struct set *set, unsigned options)
+static void set_restore_add(struct set *set, const char *adt)
 {
-       if (set == NULL) {
-               set = all_sets;
-               
-               while (set != NULL) {
-                       set_list_oneset(set, options);
-                       set = set->next;
-               }
-       } else
-               set_list_oneset(set, options);
+       DP("%s %s", set->name, adt);
+       /* Sanity checking */
+       if (restore_offset + set->settype->adt_size > restore_size)
+               exit_error(PARAMETER_PROBLEM,
+                          "Giving up, restore file is screwed up!");
+                          
+       memcpy(restore_data + restore_offset,
+              set->settype->data, set->settype->adt_size);
+       restore_set->members_size += set->settype->adt_size;
+       restore_offset += set->settype->adt_size;
 }
 
-/* Performs a save to stdout
- * All sets are marked with set.name[0] == '\0'
- * Area pointed by 'set' will be changed
+/*
+ * Send bind/unbind/test binding order to kernel for a set
  */
-static void set_save(const struct set *set)
+static int set_bind(struct set *set, const char *adt,
+                   const char *binding,
+                   unsigned op, unsigned cmd)
 {
-       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;
+       struct ip_set_req_bind *req_bind;
+       size_t size;
        void *data;
-       int i;
-
-       DP("set_addip() %p", set);
-
-       list_getheaders(set, set_ip, set_level);
-
-       parse_adt_ip(ADT_ADD, set, adt_ip);
+       int res = 0;
 
-       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;
+       /* set may be null: '-U :all: :all:|:default:' */
+       DP("(%s, %s) -> %s", set ? set->name : IPSET_TOKEN_ALL, adt, binding);
 
-       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);
-       }
+       size = sizeof(struct ip_set_req_bind);
+       if (op != IP_SET_OP_UNBIND_SET && adt[0] == ':')
+               /* Set default binding */
+               size += IP_SET_MAXNAMELEN;
+       else if (!(op == IP_SET_OP_UNBIND_SET && set == NULL))
+               size += set->settype->adt_size;
+       DP("alloc size %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;
+       /* Fill out the request */
+       req_bind = (struct ip_set_req_bind *) data;
+       req_bind->op = op;
+       req_bind->index = set ? set->index : IP_SET_INVALID_ID;
+       if (adt[0] == ':') {
+               /* ':default:' and ':all:' */
+               strncpy(req_bind->binding, adt, IP_SET_MAXNAMELEN);
+               if (op != IP_SET_OP_UNBIND_SET && adt[0] == ':')
+                       strncpy(data + sizeof(struct ip_set_req_bind),
+                               binding, IP_SET_MAXNAMELEN);
+       } else {
+               strncpy(req_bind->binding, binding, IP_SET_MAXNAMELEN);
+               memcpy(data + sizeof(struct ip_set_req_bind),
+                      set->settype->data, set->settype->adt_size);
        }
 
-#ifdef IP_SET_DEBUG
-       /* DEBUG */
-       {
-               void *i;
+       if (op == IP_SET_OP_TEST_BIND_SET) {
+               if (kernel_sendto_handleerrno(cmd, op, data, size) == -1) {
+                       ipset_printf("%s in set %s is bound to %s.",
+                                    adt, set->name, binding);
+                       res = 0;
+               } else {
+                       ipset_printf("%s in set %s is NOT bound to %s.",
+                                    adt, set->name, binding);
+                       res = 1;
+               }
+       } else  
+               kernel_sendto(cmd, data, size);
+       free(data);
 
-               DP("CMD_ADDIP");
-               DP("OP %u", req_addip.op);
-               DP("Id: %u", req_addip.id);
-               DP("All data");
+       return res;
+}
 
-               for (i = data; i < data + size; i += 4) {
-                       unsigned *j = (unsigned *) i;
-                       DP("%x", *j);
-               }
+static void set_restore_bind(struct set *set,
+                            const char *adt,
+                            const char *binding)
+{
+       struct ip_set_hash_save *hash_restore;
+
+       if (restore == 1) {
+               /* Marker */
+               struct ip_set_restore *marker = 
+                       (struct ip_set_restore *) (restore_data + restore_offset);
+
+               DP("restore marker");
+               if (restore_offset + sizeof(struct ip_set_restore) 
+                   > restore_size)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Giving up, restore file is screwed up!");
+               marker->index = IP_SET_INVALID_ID;
+               marker->header_size = marker->members_size = 0;
+               restore_offset += sizeof(struct ip_set_restore);
+               restore = 2;
        }
-#endif
+       /* Sanity checking */
+       if (restore_offset + sizeof(struct ip_set_hash_save) > restore_size)
+               exit_error(PARAMETER_PROBLEM,
+                          "Giving up, restore file is screwed up!");
+
+       hash_restore = (struct ip_set_hash_save *) (restore_data + restore_offset);
+       DP("%s -> %s", adt, binding);
+       if (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0)
+               hash_restore->ip = 0;
+       else if (set->settype->typecode == IPSET_TYPE_IP)
+               parse_ip(adt, &hash_restore->ip); 
+       else
+               parse_port(adt, &hash_restore->ip);
+       hash_restore->id = set->index;                     
+       hash_restore->binding = (set_find_byname(binding))->index;      
+       DP("id %u, ip %u, binding %u",
+          hash_restore->id, hash_restore->ip, hash_restore->binding);
+       restore_offset += sizeof(struct ip_set_hash_save);
+}
 
-       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);
+/*
+ * Print operation
+ */
+
+static void print_bindings(void *data, size_t size, unsigned options,
+                          char * (*printip)(ip_set_ip_t ip, unsigned options))
+{
+       size_t offset = 0;
+       struct ip_set_hash_list *hash;
+
+       while (offset < size) {
+               hash = (struct ip_set_hash_list *) (data + offset);
+               printf("%s -> %s\n", 
+                       printip(hash->ip, options),
+                       set_list[hash->binding]->name);
+               offset += sizeof(struct ip_set_hash_list);
+       }
 }
 
-/* Sends delip order to kernel for a set */
-static void set_delip(struct set *set, const char *adt_ip, unsigned options)
+/* Help function to set_list() */
+static size_t print_set(void *data, unsigned options)
 {
-       struct ip_set_req_std req_delip;
-       size_t size, offset;
-       void *data;
-       int i;
+       struct ip_set_list *setlist = (struct ip_set_list *) data;
+       struct set *set = set_list[setlist->index];
+       struct settype *settype = set->settype;
+       size_t offset;
 
-       DP("set_delip()");
+       /* Pretty print the set */
+       printf("Name: %s\n", set->name);
+       printf("Type: %s\n", settype->typename);
+       printf("References: %d\n", setlist->ref);
+       printf("Default binding: %s\n",
+              setlist->binding == IP_SET_INVALID_ID ? ""
+              : set_list[setlist->binding]->name);
 
-       list_getheaders(set, set_ip, set_level);
+       /* Init header */
+       offset = sizeof(struct ip_set_list);
+       settype->initheader(set, data + offset);
 
-       parse_adt_ip(ADT_DEL, set, adt_ip);
+       /* Pretty print the type header */
+       printf("Header:");
+       settype->printheader(set, options);
 
-       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;
+       /* Pretty print all IPs */
+       printf("Members:\n");
+       offset += setlist->header_size;
+       if (options & OPT_SORTED)
+               settype->printips_sorted(set, data + offset,
+                                        setlist->members_size, options);
+       else
+               settype->printips(set, data + offset,
+                                 setlist->members_size, options);
 
-       /* 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);
+       /* Print bindings */
+       printf("Bindings:\n");
+       offset += setlist->members_size;
+       print_bindings(data + offset, setlist->bindings_size, options,
+                      settype->typecode == IPSET_TYPE_IP ?
+                      ip_tostring : port_tostring);
 
-       /* 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;
-       }
+       printf("\n");           /* One newline between sets */
+       
+       return (offset + setlist->bindings_size);
+}
 
-#ifdef IP_SET_DEBUG
-       /* DEBUG */
-       {
-               void *i;
+static int try_list_sets(const char name[IP_SET_MAXNAMELEN],
+                        unsigned options)
+{
+       void *data = NULL;
+       ip_set_id_t index;
+       size_t size, req_size;
+       int res = 0;
 
-               DP("CMD_DELIP");
-               DP("OP %u", req_delip.op);
-               DP("Id: %u", req_delip.id);
-               DP("All data");
+       DP("%s", name);
+       /* Load set_list from kernel */
+       size = req_size = load_set_list(name, &index,
+                                       IP_SET_OP_LIST_SIZE, CMD_LIST);
 
-               for (i = data; i < data + size; i += 4) {
-                       unsigned *j = (unsigned *) i;
-                       DP("%x", *j);
+       if (size) {
+               /* Get sets and print them */
+               data = ipset_malloc(size);
+               ((struct ip_set_req_list *) data)->op = IP_SET_OP_LIST;
+               ((struct ip_set_req_list *) data)->index = index;
+               res = kernel_getfrom_handleerrno(CMD_LIST, data, &size);
+               DP("get_lists getsockopt() res=%d errno=%d", res, errno);
+
+               if (res != 0 || size != req_size) {
+                       free(data);
+                       return -EAGAIN;
                }
+               size = 0;
        }
-#endif
+       while (size != req_size)
+               size += print_set(data + size, options);
 
-       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);
+       ipset_free(&data);
+       return res;
 }
 
-/* Sends test order to kernel for a set */
-static int
-set_testip(struct set *set, const char *name, const char *adt_ip)
+/* Print a set or all sets
+ * All sets: name = NULL
+ */
+static void list_sets(const char name[IP_SET_MAXNAMELEN], unsigned options)
 {
-       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;
+       int i;
 
-       /* 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);
+       DP("%s", name);
+       for (i = 0; i < LIST_TRIES; i++)
+               if (try_list_sets(name, options) == 0)
+                       return;
 
-       /* 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))
+       if (errno == EAGAIN)
                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;
+                          "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(CMD_LIST, errno);
 }
 
 /* Prints help
@@ -1628,9 +1715,9 @@ set_testip(struct set *set, const char *name, const char *adt_ip)
  */
 static void set_help(const struct settype *settype)
 {
-#ifdef IP_SET_DEBUG
+#ifdef IPSET_DEBUG
        char debughelp[] =
-              "  --debug      -z              Enable debugging\n\n";
+              "  --debug      -z   Enable debugging\n\n";
 #else
        char debughelp[] = "\n";
 #endif
@@ -1639,39 +1726,47 @@ static void set_help(const struct settype *settype)
               "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 -[ADTU] set IP\n"
+              "       %s -B set IP option\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);
+              program_name, program_version, 
+              program_name, program_name, program_name,
+              program_name, program_name, program_name,
+              program_name);
 
        printf("Commands:\n"
               "Either long or short options are allowed.\n"
-              "  --create  -N setname settype0[,settype1,...] <options>\n"
+              "  --create  -N setname settype <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"
+              "  --destroy -X [setname]\n"
+              "                    Destroy a set or all sets\n"
+              "  --flush   -F [setname]\n"
+              "                    Flush a 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"
+              "  --list    -L [setname] [options]\n"
+              "                    List the IPs in a 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"
+              "  --restore -R [option]\n"
+              "                    Restores a saved state\n"
+              "  --add     -A setname IP\n"
+              "                    Add an IP to a set\n"
+              "  --del     -D setname IP\n"
+              "                    Deletes an IP from a set\n"
+              "  --test    -T setname IP \n"
+              "                    Tests if an IP exists in a set.\n"
+              "  --bind    -B setname IP|:default: -b bind-setname\n"
+              "                    Bind the IP in setname to bind-setname.\n"
+              "  --unbind  -U setname IP|:all:|:default:\n"
+              "                    Delete binding belonging to IP,\n"
+              "                    all bindings or default binding of setname.\n"
+              "  --unbind  -U :all: :all:|:default:\n"
+              "                    Delete all bindings or all default bindings.\n"
+              "  --help    -H [settype]\n"
               "                    Prints this help, and settype specific help\n"
               "  --version -V\n"
               "                    Prints version information\n\n"
@@ -1679,8 +1774,7 @@ static void set_help(const struct settype *settype)
               "  --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");
+              "  --binding    -b   Specifies the binding for -B\n");
        printf(debughelp);
 
        if (settype != NULL) {
@@ -1689,470 +1783,222 @@ static void set_help(const struct settype *settype)
        }
 }
 
-/* Hint various infos on a given settype */
-static int
-settype_hint(const struct settype *settype, unsigned options)
+static int find_cmd(const char option)
 {
-       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);
+       int i;
        
-       return 0;
+       for (i = 1; i <= NUMBER_OF_CMD; i++)
+               if (cmdflags[i] == option)
+                       return i;
+                       
+       return CMD_NONE;
 }
 
-static void init_sets(void)
+static int parse_adt_cmdline(unsigned command,
+                            const char *name,
+                            char *adt,
+                            struct set **set,
+                            struct settype **settype)
 {
-       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);
+       int res = 0;
 
-       /* Get the list of existing sets from the kernel */
-       get_sets();
-       done = 1;
+       /* -U :all: :all:|:default: */
+       if (command == CMD_UNBIND) {
+               if (strcmp(name, IPSET_TOKEN_ALL) == 0) {
+                       if (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0
+                           || strcmp(adt, IPSET_TOKEN_ALL) == 0) {
+                               *set = NULL;
+                               *settype = NULL;
+                               return 1;
+                       } else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-U %s requires %s or %s as binding name",
+                                          IPSET_TOKEN_ALL,
+                                          IPSET_TOKEN_DEFAULT,
+                                          IPSET_TOKEN_ALL);
+               }
+       }
+       *set = restore ? set_find_byname(name)
+                      : set_adt_get(name);
+                                       
+       /* Reset space for adt data */
+       *settype = (*set)->settype;
+       memset((*settype)->data, 0, (*settype)->adt_size);
+
+       if ((command == CMD_TEST
+            || command == CMD_BIND
+            || command == CMD_UNBIND)
+           && (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0
+               || strcmp(adt, IPSET_TOKEN_ALL) == 0))
+               res = 1;
+       else
+               res = (*settype)->adt_parser(
+                               command,
+                               adt,
+                               (*settype)->data);
+
+       return res;
 }
 
 /* Main worker function */
-int parse_commandline(int argc, char *argv[], int exec_restore)
+int parse_commandline(int argc, char *argv[])
 {
        int res = 0;
-       unsigned command = 0;
+       unsigned command = CMD_NONE;
        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 */
+       
+       FILE *in = stdin;               /* -R */
+
+       char *name = NULL;              /* All except -H, -R */
+       char *newname = NULL;           /* -E, -W */
+       char *adt = NULL;               /* -A, -D, -T, -B, -U */
+       char *binding = NULL;           /* -B */
+       struct set *set = NULL;         /* -A, -D, -T, -B, -U */
+       struct settype *settype = NULL; /* -N, -H */
+       char all_sets[] = IPSET_TOKEN_ALL;
        
        struct option *opts = opts_long;
 
        /* Suppress error messages: we may add new options if we
           demand-load a protocol. */
        opterr = 0;
-
+       /* Reset optind to 0 for restore */
+       optind = 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.
+                        * Command selection
                         */
                case 'h':
-               case 'H':{      /* Help */
+               case 'H':{      /* Help: -H [typename [options]] */
+                               check_protocolversion();
                                set_command(&command, CMD_HELP);
                                
                                if (optarg)
-                                       set_checktype(optarg);
+                                       settype = check_set_typename(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);
-                               }
+                                       settype = check_set_typename(argv[optind++]);
                                
                                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);
+                               check_protocolversion();
                                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");
+               case 'N':{      /* Create: -N name typename options */
+                               set_command(&command, CMD_CREATE);
 
-                                       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);
-                               }
+                               name = check_set_name(optarg);
+                               
+                               /* Protect reserved names (binding) */
+                               if (name[0] == ':')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "setname might not start with colon",
+                                                  cmd2char(CMD_CREATE));
+                               
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       settype = check_set_typename(argv[optind++]);
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires setname and settype",
+                                                  cmd2char(CMD_CREATE));
 
                                DP("merge options");
                                /* Merge the create options */
                                opts = merge_options(opts,
-                                            set->settype[set_level]->create_opts,
-                                            &set->settype[set_level]->option_offset);
+                                            settype->create_opts,
+                                            &settype->option_offset);
 
-                               /* Reset space for settype create data */
-                               memset(set->settype[set_level]->data, 0,
-                                      set->settype[set_level]->create_size);
+                               /* Reset space for create data */
+                               memset(settype->data, 0, settype->create_size);
 
                                /* Zero the flags */
-                               set->settype[set_level]->flags = 0;
+                               settype->flags = 0;
 
                                DP("call create_init");
                                /* Call the settype create_init */
-                               set->settype[set_level]->create_init(
-                                               set->settype[set_level]->data);
+                               settype->create_init(settype->data);
 
                                break;
                        }
 
-               case 'X':{      /* Destroy */
-                               init_sets();
-                               
-                               set_command(&command, CMD_DESTROY);
+               case 'X':       /* Destroy */
+               case 'F':       /* Flush */
+               case 'L':       /* List */
+               case 'S':{      /* Save */
+                               set_command(&command, find_cmd(c));
 
                                if (optarg)
-                                       set = set_find_byname(optarg);
+                                       name = check_set_name(optarg);
                                else if (optind < argc
                                           && argv[optind][0] != '-')
-                                       set = set_find_byname(argv[optind++]);
+                                       name = check_set_name(argv[optind++]);
                                else
-                                       set = NULL;     /* Mark to destroy all (empty) sets */
+                                       name = all_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 */
+               case 'R':{      /* Restore */
+                               set_command(&command, find_cmd(c));
 
-                               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");
+               case 'E':       /* Rename */
+               case 'W':{      /* Swap */
+                               set_command(&command, find_cmd(c));
+                               name = check_set_name(optarg);
 
                                if (optind < argc
                                    && argv[optind][0] != '-')
-                                       name = argv[optind++];
+                                       newname = check_set_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);
+               case 'A':       /* Add IP */
+               case 'D':       /* Del IP */
+               case 'T':       /* Test IP */
+               case 'B':       /* Bind IP */
+               case 'U':{      /* Unbind IP */
+                               set_command(&command, find_cmd(c));
 
-                               set = set_find_byname(optarg);
-
-                               if (set_level >= set->levels)
-                                       exit_error(PARAMETER_PROBLEM,
-                                                  "specified childset is deeper than "
-                                                  "%s set itself.", set->name);
+                               name = check_set_name(optarg);
 
+                               /* IP */
                                if (optind < argc
                                    && argv[optind][0] != '-')
-                                       entries = argv[optind++];
+                                       adt = argv[optind++];
                                else
                                        exit_error(PARAMETER_PROBLEM,
-                                                  "-%c requires setname and entries",
-                                                  cmd2char(CMD_DEL));
+                                                  "-%c requires setname and IP",
+                                                  c);
 
-                               break;
-                       }
+                               res = parse_adt_cmdline(command, name, adt,
+                                                       &set, &settype);
 
-               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
+                               if (!res)
                                        exit_error(PARAMETER_PROBLEM,
-                                                  "-%c requires setname and entries",
-                                                  cmd2char(CMD_TEST));
+                                                  "Unknown arg `%s'",
+                                                  argv[optind - 1]);
 
                                break;
                        }
@@ -2172,19 +2018,16 @@ int parse_commandline(int argc, char *argv[], int exec_restore)
                        option_quiet = 1;
                        break;
 
-#ifdef IP_SET_DEBUG
+#ifdef IPSET_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);
+               case 'b':
+                       add_option(&options, OPT_BINDING);
+                       binding = check_set_name(optarg);
                        break;
 
                case 1: /* non option */
@@ -2197,31 +2040,13 @@ int parse_commandline(int argc, char *argv[], int exec_restore)
 
                                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(
+                                       res = settype->create_parse(
                                                        c - settype->option_offset,
                                                        argv,
-                                                       settype->data);
+                                                       settype->data,
+                                                       &settype->flags);
                                        break;
-                                       }
+
                                default:
                                        res = 0;        /* failed */
                                }       /* switch (command) */
@@ -2243,75 +2068,90 @@ int parse_commandline(int argc, char *argv[], int exec_restore)
        if (optind < argc)
                exit_error(PARAMETER_PROBLEM,
                           "unknown arguments found on commandline");
-       if (!command)
+       if (command == CMD_NONE)
                exit_error(PARAMETER_PROBLEM, "no command specified");
 
        /* Check options */
-       generic_opt_check(command == CMD_CREATE_CHILD ? CMD_CREATE : command, options);
+       generic_opt_check(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);
+               if (restore)
+                       set_restore_create(name, settype);
+               else
+                       set_create(name, settype);
                break;
 
        case CMD_DESTROY:
-               set_destroy(set);
+               set_destroy(name, IP_SET_OP_DESTROY, CMD_DESTROY);
                break;
 
        case CMD_FLUSH:
-               set_flush(set, options);
+               set_destroy(name, IP_SET_OP_FLUSH, CMD_FLUSH);
                break;
 
        case CMD_RENAME:
-               set_rename(set, name);
+               set_rename(name, newname, IP_SET_OP_RENAME, CMD_RENAME);
                break;
 
        case CMD_SWAP:
-               set_swap(set, set_to);
+               set_rename(name, newname, IP_SET_OP_SWAP, CMD_SWAP);
                break;
 
        case CMD_LIST:
-               set_list(set, options);
+               list_sets(name, options);
                break;
 
        case CMD_SAVE:
-               set_save(set);
+               set_save(name);
                break;
 
        case CMD_RESTORE:
-               set_restore();
+               set_restore(in, argv[0]);
                break;
 
        case CMD_ADD:
-               set_addip(set, entries);
+               if (restore)
+                       set_restore_add(set, adt);
+               else
+                       set_adtip(set, adt, IP_SET_OP_ADD_IP, CMD_ADD);
                break;
 
        case CMD_DEL:
-               set_delip(set, entries, options);
+               set_adtip(set, adt, IP_SET_OP_DEL_IP, CMD_DEL);
                break;
 
        case CMD_TEST:
-               res = set_testip(set, name, entries);
+               if (binding)
+                       res = set_bind(set, adt, binding, 
+                                      IP_SET_OP_TEST_BIND_SET, CMD_TEST);
+               else
+                       res = set_adtip(set, adt, 
+                                       IP_SET_OP_TEST_IP, CMD_TEST);
                break;
 
-       case CMD_HELP:
-               if (options & OPT_HINT)
-                       res = settype_hint(settype, options);
+       case CMD_BIND:
+               if (restore)
+                       set_restore_bind(set, adt, binding);
                else
-                       set_help(settype);
+                       set_bind(set, adt, binding,
+                                IP_SET_OP_BIND_SET, CMD_BIND);
+               break;
+
+       case CMD_UNBIND:
+               set_bind(set, adt, "", IP_SET_OP_UNBIND_SET, CMD_UNBIND);
+               break;
+
+       case CMD_HELP:
+               set_help(settype);
                break;
 
        default:
                /* Will never happen */
-               ; /* Keep the compiler happy */
+               break; /* Keep the compiler happy */
 
        }       /* switch( command ) */
 
@@ -2320,7 +2160,7 @@ int parse_commandline(int argc, char *argv[], int exec_restore)
 
 
 int main(int argc, char *argv[])
-{
-       return parse_commandline(argc, argv, 0);
+{      
+       return parse_commandline(argc, argv);
 
 }
diff --git a/ipset.h b/ipset.h
index 095f75650a6785ce96f6268fa4a8c16c18d6f4a4..e563331d64aa607c9c1d251ab8b5a41dbac8562c 100644 (file)
--- a/ipset.h
+++ b/ipset.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
+#ifdef IPSET_DEBUG
 extern int option_debug;
 #define DP(format, args...) if (option_debug)                  \
        do {                                                    \
@@ -50,34 +42,47 @@ extern int option_debug;
 #define DP(format, args...)
 #endif
 
+/* Commands */
+enum set_commands {
+       CMD_NONE,
+       CMD_CREATE,             /* -N */
+       CMD_DESTROY,            /* -X */
+       CMD_FLUSH,              /* -F */
+       CMD_RENAME,             /* -E */
+       CMD_SWAP,               /* -W */
+       CMD_LIST,               /* -L */
+       CMD_SAVE,               /* -S */
+       CMD_RESTORE,            /* -R */
+       CMD_ADD,                /* -A */
+       CMD_DEL,                /* -D */
+       CMD_TEST,               /* -T */
+       CMD_BIND,               /* -B */
+       CMD_UNBIND,             /* -U */
+       CMD_HELP,               /* -H */
+       CMD_VERSION,            /* -V */
+       NUMBER_OF_CMD = CMD_VERSION,
+       /* Internal commands */
+       CMD_MAX_SETS,
+       CMD_LIST_SIZE,
+       CMD_SAVE_SIZE,
+       CMD_ADT_GET,
+};
+
 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 */
+/* The view of an ipset in userspace */
 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 */
+       ip_set_id_t id;                         /* Unique set id */
+       ip_set_id_t index;                      /* Array index */
        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 */
+       struct settype *settype;                /* Pointer to set type functions */
 };
 
-#define ADT_ADD                0
-#define ADT_DEL                1
-#define ADT_TEST       2
-
 struct settype {
        struct settype *next;
 
@@ -106,79 +111,49 @@ struct settype {
        /* 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;
+       size_t adt_size;
 
        /* Function which parses command options */
-       ip_set_ip_t (*adt_parser) (int cmd, const char *option, 
-                                  void *data, const void * setdata);
+       ip_set_ip_t (*adt_parser) (unsigned cmd, const char *optarg, void *data);
 
        /*
         * 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);
+       /* Size of header. */
+       size_t header_size;
 
-       /* Remove the members memory usage */
-       void (*killmembers) (void ** setdata);
+       /* Initialize the type-header */
+       void (*initheader) (struct set *set, const void *data);
 
        /* 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);
-       
+       void (*printheader) (struct set *set, unsigned options);
+
        /* Pretty print all IPs */
-       void (*printips) (const void * setdata, unsigned options);
+       void (*printips) (struct set *set, void *data, size_t len, unsigned options);
 
        /* Pretty print all IPs sorted */
-       void (*printips_sorted) (const void * setdata,
-                                unsigned options);
+       void (*printips_sorted) (struct set *set, void *data, size_t len, unsigned options);
 
        /* Print save arguments for creating the set */
-       void (*saveheader) (const void * setdata);
+       void (*saveheader) (struct set *set, unsigned options);
 
        /* Print save for all IPs */
-       void (*saveips) (const void * setdata);
+       void (*saveips) (struct set *set, void *data, size_t len, unsigned options);
 
        /* 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 */
+       void *header;
+       void *data;
        unsigned int option_offset;
        unsigned int flags;
-       void *data;
 };
 
 extern void settype_register(struct settype *settype);
index ea182969d1c24a9ef38633c11246f517f46158b7..8d6cda6350975daefe604f00598d8a250fecffa7 100644 (file)
 
 #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
+#define OPT_CREATE_HASHSIZE    0x01U
+#define OPT_CREATE_PROBES      0x02U
+#define OPT_CREATE_RESIZE      0x04U
+#define OPT_CREATE_NETMASK     0x08U
 
 /* Initialize the create. */
 void create_init(void *data)
@@ -52,8 +47,12 @@ void create_init(void *data)
            (struct ip_set_req_iphash_create *) data;
 
        DP("create INIT");
+
+       /* Default create parameters */ 
+       mydata->hashsize = 1024;
+       mydata->probes = 8;
+       mydata->resize = 50;
        
-       mydata->initval = 0;
        mydata->netmask = 0xFFFFFFFF;
 }
 
@@ -62,36 +61,49 @@ 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;
+       ip_set_ip_t value;
 
        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;
+               if (string_to_number(optarg, 1, UINT_MAX - 1, &mydata->hashsize))
+                       exit_error(PARAMETER_PROBLEM, "Invalid hashsize `%s' specified", optarg);
+
+               *flags |= OPT_CREATE_HASHSIZE;
+
+               DP("--hashsize %u", mydata->hashsize);
+               
+               break;
 
-                       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);
+               if (string_to_number(optarg, 1, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid probes `%s' specified", optarg);
 
-               *flags |= OPT_CREATE_HASHSIZE;
+               mydata->probes = value;
+               *flags |= OPT_CREATE_PROBES;
 
-               DP("--hashsize %u", mydata->hashsize);
+               DP("--probes %u", mydata->probes);
                
                break;
 
        case '3':
 
+               if (string_to_number(optarg, 0, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid resize `%s' specified", optarg);
+
+               mydata->resize = value;
+               *flags |= OPT_CREATE_RESIZE;
+
+               DP("--resize %u", mydata->resize);
+               
+               break;
+
+       case '4':
+
                if (string_to_number(optarg, 0, 32, &bits))
                        exit_error(PARAMETER_PROBLEM, 
                                  "Invalid netmask `%s' specified", optarg);
@@ -118,119 +130,46 @@ 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);
+       DP("hashsize %u probes %u resize %u",
+          mydata->hashsize, mydata->probes, mydata->resize);
 }
 
 /* Create commandline options */
 static struct option create_opts[] = {
-       {"initval", 1, 0, '1'},
-       {"hashsize", 1, 0, '2'},
-       {"netmask", 1, 0, '3'},
+       {"hashsize", 1, 0, '1'},
+       {"probes", 1, 0, '2'},
+       {"resize", 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)
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
 {
        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);
-}
+/*
+ * Print and save
+ */
 
-void initheader(void **setdata, void *data, size_t len)
+void initheader(struct set *set, const void *data)
 {
        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);
+       struct ip_set_iphash *map =
+               (struct ip_set_iphash *) set->settype->header;
+
+       memset(map, 0, sizeof(struct ip_set_iphash));
+       map->hashsize = header->hashsize;
+       map->probes = header->probes;
+       map->resize = header->resize;
+       map->netmask = header->netmask;
 }
 
 unsigned int
@@ -249,188 +188,71 @@ mask_to_bits(ip_set_ip_t mask)
        return bits;
 }
        
-void printheader(const void *setdata, unsigned options)
+void printheader(struct set *set, unsigned options)
 {
        struct ip_set_iphash *mysetdata =
-           (struct ip_set_iphash *) setdata;
+           (struct ip_set_iphash *) set->settype->header;
 
-       printf(" initval: 0x%x", mysetdata->initval);
-       printf(" hashsize: %d", mysetdata->hashsize);
+       printf(" hashsize: %u", mysetdata->hashsize);
+       printf(" probes: %u", mysetdata->probes);
+       printf(" resize: %u", mysetdata->resize);
        if (mysetdata->netmask == 0xFFFFFFFF)
                printf("\n");
        else
                printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask));
 }
 
-void printips(const void *setdata, unsigned options)
+void printips(struct set *set, void *data, size_t len, 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));
+       size_t offset = 0;
+       ip_set_ip_t *ip;
+
+       while (offset < len) {
+               ip = data + offset;
+               if (*ip)
+                       printf("%s\n", ip_tostring(*ip, options));
+               offset += sizeof(ip_set_ip_t);
+       }
 }
 
-void saveheader(const void *setdata)
+void saveheader(struct set *set, unsigned options)
 {
-       return;
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) set->settype->header;
+
+       printf("-N %s %s --hashsize %u --probes %u --resize %u",
+              set->name, set->settype->typename,
+              mysetdata->hashsize, mysetdata->probes, mysetdata->resize);
+       if (mysetdata->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" --netmask %d\n", mask_to_bits(mysetdata->netmask));
 }
 
 /* Print save for an IP */
-void saveips(const void *setdata)
+void saveips(struct set *set, void *data, size_t len, unsigned options)
 {
-       return;
+       size_t offset = 0;
+       ip_set_ip_t *ip;
+
+       while (offset < len) {
+               ip = data + offset;
+               if (*ip)
+                       printf("-A %s %s\n", set->name, ip_tostring(*ip, options));
+               offset += sizeof(ip_set_ip_t);
+       }
 }
 
 void usage(void)
 {
        printf
-           ("-N set iphash [--initval hash-initval] --hashsize hashsize [--netmask CIDR-netmask]\n"
-            "-A set [!]IP\n"
+           ("-N set iphash [--hashsize hashsize] [--probes probes ]\n"
+            "              [--resize resize] [--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;       
+            "-T set IP\n");
 }
 
-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,
@@ -444,30 +266,18 @@ static struct settype settype_iphash = {
        .create_opts = create_opts,
 
        /* Add/del/test */
-       .req_size = sizeof(struct ip_set_req_iphash),
+       .adt_size = sizeof(struct ip_set_req_iphash),
        .adt_parser = &adt_parser,
 
-       /* Get an IP address by id */
-       .getipbyid = &getipbyid,
-       .sizeid = &sizeid,
-
        /* Printing */
+       .header_size = sizeof(struct ip_set_iphash),
        .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)
index df03a14df88c59d7f8475dbbd305451002ad4799..7da8f1ec4a6dabd0c379505774594ccfb259031e 100644 (file)
@@ -18,7 +18,7 @@
  */
 
 #include <stdio.h>
-#include <strings.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -183,66 +183,31 @@ static struct option create_opts[] = {
 };
 
 /* Add, del, test parser */
-ip_set_ip_t adt_parser(int cmd, const char *optarg,
-                      void *data, const void *setdata)
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
 {
        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);
+       DP("ipmap: %p %p", optarg, data);
 
        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));
+       DP("%s", 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);
-}
+/*
+ * Print and save
+ */
 
-void initheader(void **setdata, void *data, size_t len)
+void initheader(struct set *set, const void *data)
 {
        struct ip_set_req_ipmap_create *header =
            (struct ip_set_req_ipmap_create *) data;
-       struct ip_set_ipmap *map;
+       struct ip_set_ipmap *map =
+               (struct ip_set_ipmap *) set->settype->header;
                
-       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;
-       
+       memset(map, 0, sizeof(struct ip_set_ipmap));
        map->first_ip = header->from;
        map->last_ip = header->to;
        map->netmask = header->netmask;
@@ -263,75 +228,66 @@ void initheader(void **setdata, void *data, size_t len)
        }
 
        DP("%i %i", map->hosts, map->sizeid );
-
 }
 
-void initmembers(void *setdata, void *data, size_t len)
+void printheader(struct set *set, unsigned options)
 {
        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);
+           (struct ip_set_ipmap *) set->settype->header;
 
-       if (len != size)
-               exit_error(OTHER_PROBLEM,
-                          "Ipmap: incorrect size of members. "
-                          "Got %d, wanted %d.", len, size);
-
-       mysetdata->members = data;
+       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 killmembers(void **setdata)
+void printips_sorted(struct set *set, void *data, size_t len, unsigned options)
 {
        struct ip_set_ipmap *mysetdata =
-           (struct ip_set_ipmap *) *setdata;
-
-       DP("ipmap: killmembers()");
+           (struct ip_set_ipmap *) set->settype->header;
+       ip_set_ip_t id;
 
-       if (mysetdata->members != NULL)
-               ipset_free(&mysetdata->members);
-               
-       ipset_free(setdata);
+       for (id = 0; id < mysetdata->sizeid; id++)
+               if (test_bit(id, data))
+                       printf("%s\n",
+                              ip_tostring(mysetdata->first_ip
+                                          + id * mysetdata->hosts,
+                                          options));
 }
 
-void printheader(const void *setdata, unsigned options)
+void saveheader(struct set *set, unsigned options)
 {
        struct ip_set_ipmap *mysetdata =
-           (struct ip_set_ipmap *) setdata;
+           (struct ip_set_ipmap *) set->settype->header;
 
-       printf(" from: %s", ip_tostring(mysetdata->first_ip, options));
-       printf(" to: %s", ip_tostring(mysetdata->last_ip, options));
+       printf("-N %s %s --from %s",
+              set->name, set->settype->typename,
+              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));
+               printf(" --netmask %d\n",
+                      mask_to_bits(mysetdata->netmask));
 }
 
-void printips_sorted(const void *setdata, unsigned options)
+void saveips(struct set *set, void *data, size_t len, unsigned options)
 {
        struct ip_set_ipmap *mysetdata =
-           (struct ip_set_ipmap *) setdata;
+           (struct ip_set_ipmap *) set->settype->header;
        ip_set_ip_t id;
 
+       DP("%s", set->name);
        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;
+               if (test_bit(id, data))
+                       printf("-A %s %s\n",
+                              set->name,
+                              ip_tostring(mysetdata->first_ip 
+                                          + id * mysetdata->hosts,
+                                          options));
 }
 
 void usage(void)
@@ -357,24 +313,18 @@ static struct settype settype_ipmap = {
        .create_opts = create_opts,
 
        /* Add/del/test */
-       .req_size = sizeof(struct ip_set_req_ipmap),
+       .adt_size = sizeof(struct ip_set_req_ipmap),
        .adt_parser = &adt_parser,
 
-       /* Get an IP address by id */
-       .getipbyid = &getipbyid,
-       .sizeid = &sizeid,
-
        /* Printing */
+       .header_size = sizeof(struct ip_set_ipmap),
        .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)
index eff42ceb0d908b6cf1e18937d3eb297dece03079..5d12af458bed87a1c3e1d58adf85f36e6c254218 100644 (file)
@@ -175,119 +175,47 @@ static void parse_mac(const char *mac, unsigned char *ethernet)
 }
 
 /* Add, del, test parser */
-ip_set_ip_t adt_parser(int cmd, const char *optarg,
-                      void *data, const void *setdata)
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
 {
        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);
+       DP("macipmap: %p %p", optarg, data);
 
        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);
-}
+/*
+ * Print and save
+ */
 
-void initheader(void **setdata, void *data, size_t len)
+void initheader(struct set *set, const void *data)
 {
        struct ip_set_req_macipmap_create *header =
            (struct ip_set_req_macipmap_create *) data;
+       struct ip_set_macipmap *map =
+               (struct ip_set_macipmap *) set->settype->header;
 
-       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;
+       memset(map, 0, sizeof(struct ip_set_macipmap));
+       map->first_ip = header->from;
+       map->last_ip = header->to;
+       map->flags = header->flags;
 }
 
-void killmembers(void **setdata)
+void printheader(struct set *set, unsigned options)
 {
        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;
+           (struct ip_set_macipmap *) set->settype->header;
 
        printf(" from: %s", ip_tostring(mysetdata->first_ip, options));
        printf(" to: %s", ip_tostring(mysetdata->last_ip, options));
@@ -306,18 +234,18 @@ static void print_mac(unsigned char macaddress[ETH_ALEN])
                printf(":%02X", macaddress[i]);
 }
 
-void printips_sorted(const void *setdata, unsigned options)
+void printips_sorted(struct set *set, void *data, size_t len, unsigned options)
 {
        struct ip_set_macipmap *mysetdata =
-           (struct ip_set_macipmap *) setdata;
+           (struct ip_set_macipmap *) set->settype->header;
        struct ip_set_macip *table =
-           (struct ip_set_macip *) mysetdata->members;
+           (struct ip_set_macip *) data;
        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), "%");
+                            (void *)&table[addr - mysetdata->first_ip].flags)) {
+                       printf("%s%%", ip_tostring(addr, options));
                        print_mac(table[addr - mysetdata->first_ip].
                                  ethernet);
                        printf("\n");
@@ -326,15 +254,40 @@ void printips_sorted(const void *setdata, unsigned options)
        }
 }
 
-void saveheader(const void *setdata)
+void saveheader(struct set *set, unsigned options)
 {
-       return;
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) set->settype->header;
+
+       printf("-N %s %s --from: %s",
+              set->name, set->settype->typename,
+              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");
 }
 
-/* Print save for an IP */
-void saveips(const void *setdata)
+void saveips(struct set *set, void *data, size_t len, unsigned options)
 {
-       return;
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) set->settype->header;
+       struct ip_set_macip *table =
+           (struct ip_set_macip *) data;
+       u_int32_t addr = mysetdata->first_ip;
+
+       while (addr <= mysetdata->last_ip) {
+               if (test_bit(IPSET_MACIP_ISSET,
+                            (void *)&table[addr - mysetdata->first_ip].flags)) {
+                       printf("-A %s %s%%",
+                              set->name, ip_tostring(addr, options));
+                       print_mac(table[addr - mysetdata->first_ip].
+                                 ethernet);
+                       printf("\n");
+               }
+               addr++;
+       }
 }
 
 void usage(void)
@@ -360,24 +313,18 @@ static struct settype settype_macipmap = {
        .create_opts = create_opts,
 
        /* Add/del/test */
-       .req_size = sizeof(struct ip_set_req_macipmap),
+       .adt_size = sizeof(struct ip_set_req_macipmap),
        .adt_parser = &adt_parser,
 
-       /* Get an IP address by id */
-       .getipbyid = &getipbyid,
-       .sizeid = &sizeid,
-
        /* Printing */
+       .header_size = sizeof(struct ip_set_macipmap),
        .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)
index fd8746f75e718f7877523e651b96d53d1c54e10a..ba9e651c68c2f766cf709be681c7572e71d2d8c6 100644 (file)
@@ -17,6 +17,7 @@
 
 
 #include <stdio.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -114,134 +115,82 @@ static struct option create_opts[] = {
 };
 
 /* Add, del, test parser */
-ip_set_ip_t adt_parser(int cmd, const char *optarg,
-                      void *data, const void *setdata)
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
 {
        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));
+       DP("%s", 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);
-}
+/*
+ * Print and save
+ */
 
-void initheader(void **setdata, void *data, size_t len)
+void initheader(struct set *set, const void *data)
 {
        struct ip_set_req_portmap_create *header =
            (struct ip_set_req_portmap_create *) data;
+       struct ip_set_portmap *map =
+               (struct ip_set_portmap *) set->settype->header;
 
-       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;
-
+       memset(map, 0, sizeof(struct ip_set_portmap));
+       map->first_port = header->from;
+       map->last_port = header->to;
 }
 
-void initmembers(void *setdata, void *data, size_t len)
+void printheader(struct set *set, unsigned options)
 {
        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;
+           (struct ip_set_portmap *) set->settype->header;
 
        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)
+void printports_sorted(struct set *set, void *data, size_t len, unsigned options)
 {
        struct ip_set_portmap *mysetdata =
-           (struct ip_set_portmap *) setdata;
-
+           (struct ip_set_portmap *) set->settype->header;
        u_int32_t addr = mysetdata->first_port;
 
+       DP("%u -- %u", mysetdata->first_port, mysetdata->last_port);
        while (addr <= mysetdata->last_port) {
-               if (test_bit(addr - mysetdata->first_port, mysetdata->members))
+               if (test_bit(addr - mysetdata->first_port, data))
                        printf("%s\n", port_tostring(addr, options));
                addr++;
        }
 }
 
-void saveheader(const void *setdata)
+void saveheader(struct set *set, unsigned options)
 {
-       return;
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) set->settype->header;
+
+       printf("-N %s %s --from %s", 
+              set->name,
+              set->settype->typename,
+              port_tostring(mysetdata->first_port, options));
+       printf(" --to %s\n", 
+              port_tostring(mysetdata->last_port, options));
 }
 
-/* Print save for an IP */
-void saveports(const void *setdata)
+void saveports(struct set *set, void *data, size_t len, unsigned options)
 {
-       return;
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) set->settype->header;
+       u_int32_t addr = mysetdata->first_port;
+
+       while (addr <= mysetdata->last_port) {
+               if (test_bit(addr - mysetdata->first_port, data))
+                       printf("-A %s %s\n",
+                              set->name,
+                              port_tostring(addr, options));
+               addr++;
+       }
 }
 
 void usage(void)
@@ -266,24 +215,18 @@ static struct settype settype_portmap = {
        .create_opts = create_opts,
 
        /* Add/del/test */
-       .req_size = sizeof(struct ip_set_req_portmap),
+       .adt_size = sizeof(struct ip_set_req_portmap),
        .adt_parser = &adt_parser,
 
-       /* Get an IP address by id */
-       .getipbyid = &getportbyid,
-       .sizeid = &sizeid,
-
        /* Printing */
+       .header_size = sizeof(struct ip_set_portmap),
        .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)
index 8180f7cd173d918aa798c0d7cc92ef125de0c812..0d5ab5d1a085d7e708eec989071ff6f8c48edeb6 100644 (file)
@@ -16,117 +16,30 @@ static int get_set_getsockopt(void *data, size_t * size)
        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)
+parse_bindings(const char *optarg, struct ipt_set_info *info)
 {
        char *saved = strdup(optarg);
        char *ptr, *tmp = saved;
-
-       ptr = strsep(&tmp, ":");
-       get_set_byname(ptr, info);
+       int i = 0;
        
-       while (info->set_level < IP_SET_SETIP_LEVELS && tmp) {
+       while (i < IP_SET_MAX_BINDINGS && tmp != NULL) {
                ptr = strsep(&tmp, ",");
                if (strncmp(ptr, "src", 3) == 0)
-                       info->flags[info->set_level++] |= IPSET_SRC;
+                       info->flags[i++] |= IPSET_SRC;
                else if (strncmp(ptr, "dst", 3) == 0)
-                       info->flags[info->set_level++] |= IPSET_DST;
+                       info->flags[i++] |= 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)
+       if (tmp)
                exit_error(PARAMETER_PROBLEM,
-                          "Defined childset level is deeper that %i.", 
-                          IP_SET_SETIP_LEVELS);
+                          "Can't follow bindings deeper than %i.", 
+                          IP_SET_MAX_BINDINGS - 1);
 
        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*/