ipset-2.2.8-20051203
authorMark Huang <mlhuang@cs.princeton.edu>
Mon, 20 Feb 2006 19:02:24 +0000 (19:02 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Mon, 20 Feb 2006 19:02:24 +0000 (19:02 +0000)
15 files changed:
ipset/ChangeLog [new file with mode: 0644]
ipset/ChangeLog.ippool [new file with mode: 0644]
ipset/Makefile [new file with mode: 0644]
ipset/TODO [new file with mode: 0644]
ipset/ipset.8 [new file with mode: 0644]
ipset/ipset.c [new file with mode: 0644]
ipset/ipset.h [new file with mode: 0644]
ipset/ipset_iphash.c [new file with mode: 0644]
ipset/ipset_ipmap.c [new file with mode: 0644]
ipset/ipset_ipporthash.c [new file with mode: 0644]
ipset/ipset_iptree.c [new file with mode: 0644]
ipset/ipset_macipmap.c [new file with mode: 0644]
ipset/ipset_nethash.c [new file with mode: 0644]
ipset/ipset_portmap.c [new file with mode: 0644]
ipset/libipt_set.h [new file with mode: 0644]

diff --git a/ipset/ChangeLog b/ipset/ChangeLog
new file mode 100644 (file)
index 0000000..5cce591
--- /dev/null
@@ -0,0 +1,90 @@
+2.2.8
+ - Nasty off-by-one bug fixed in iptree type of sets
+   (bug reported by Pablo Sole)
+
+2.2.7
+ All patches were submitted by Jones Desougi
+ - missing or confusing error message fixes for ipporthash
+ - minor correction in debugging in nethash
+ - copy-paste bug in kernel set types at memory allocation
+   checking fixed
+ - unified memory allocations in ipset
+
+2.2.6
+ - memory allocation in iptree is changed to GFP_ATOMIC because
+   we hold a lock (bug reported by Radek Hladik)
+ - compatibility fix: __nocast is not defined in all 2.6 branches
+   (problem reported by Ming-Ching Tiew)
+ - manpage corrections
+
+2.2.5
+ - garbage collector of iptree type of sets is fixed: flushing
+   sets/removing kernel module could corrupt the timer
+ - new ipporthash type added
+ - manpage fixes and corrections
+
+2.2.4
+ - half-fixed memory allocation bug in iphash and nethash finally
+   completely fixed (bug reported by Nikolai Malykh)
+ - restrictions to enter zero-valued entries into all non-hash type sets
+   were removed
+ - Too strict check on the set size of ipmap type was corrected 
+
+2.2.3
+ - memory allocation bug in iphash and nethash in connection with the SET
+   target was fixed (bug reported by Nikolai Malykh)
+ - lockhelp.h was removed from the 2.6.13 kernel tree, ip_set.c is
+   updated accordingly (Cardoso Didier, Samir Bellabes)
+ - manpage is updated to clearly state the command order in restore mode
+
+2.2.2
+ - Jiffies rollover bug in ip_set_iptree reported and fixed by Rob Nielsen
+ - Compiler warning in the non-SMP case fixed (Marcus Sundberg)
+ - slab cache names shrunk in order to be compatible with 2.4.* (Marcus
+   Sundberg)
+
+2.2.1
+ - Magic number in ip_set_nethash.h was mistyped (bug reported by Rob
+   Carlson)
+ - ipset can now test IP addresses in nethash type of sets (i.e. addresses
+   in netblocks added to the set)
+
+2.2.0
+ - Locking bug in ip_set_nethash.c (Clifford Wolf and Rob Carlson)
+ - Makefile contained an unnecessary variable in IPSET_LIB_DIR (Clifford
+   Wolf)
+ - Safety checkings of restore in ipset was incomplete (Robin H. Johnson)
+ - More careful resizing by avoiding locking completely
+ - stdin stored internally in a temporary file, so we can feed 'ipset -R'
+   from a pipe
+ - iptree maptype added
+
+2.1
+ - Lock debugging used with debugless lock definiton (Piotr Chytla and
+   others).
+ - Bindings were not properly filled out at listing (kernel)
+ - When listing sets from kernel, id was not added to the set structure
+   (ipset)
+ - nethash maptype added
+ - ipset manpage corrections (macipmap)
+
+2.0.1
+ - Missing -fPIC in Makefile (Robert Iakobashvili)
+ - Cut'n'paste bug at saving macipmap types (Vincent Bernat).
+ - Bug in printing/saving SET targets reported and fixed by Michal
+   Pokrywka
+
+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
+ - Rewritten to support child pools
+ - portmap, iphash pool support added
+ - too much other mods here and there to list...
+
diff --git a/ipset/ChangeLog.ippool b/ipset/ChangeLog.ippool
new file mode 100644 (file)
index 0000000..669c304
--- /dev/null
@@ -0,0 +1,59 @@
+Original changelog as ippool:
+
+0.3.2b
+- Fixed missing kfree(pool)                              (Martin Josefsson)
+
+0.3.2a
+- Added libipt_pool.c and libipt_POOL.c                          (Martin Josefsson)
+
+
+0.3.2
+- Passes pointers to skb's around instead of ip's in the  (Martin Josefsson)
+  kernel modules.
+- Added a new pooltype, macipmap, which matches ip's      (Martin Josefsson)
+  against macaddresses.
+- Cleaned up a lot of typedefs.                           (Martin Josefsson)
+- Fixed an unlocking of the wrong lock.                   (Martin Josefsson)
+- Fixed a refcount bug when allocated memory was too      (Martin Josefsson)
+  small.
+- Fixed a free() of unallocated memory.                   (Martin Josefsson)
+- Switched from kmalloc/kfree to vmalloc/vfree for        (Martin Josefsson)
+  pool-listings/additions.
+
+
+0.3.1
+- Changed the API between userspace modules and base.     (Joakim Axelsson)
+  Moved the memberdata pointer to module self.
+  As a result of this Protocolversion is increased to 4.
+- Fixed problems with crashing null-pooltype              (Joakim Axelsson)
+- Fixed problems with compiling warnings                  (Joakim Axelsson)
+  in null pooltype.
+
+       
+0.3.0:
+- Changed the listing to use getsockopt.                   (Joakim Axelsson)
+  /proc is left for debuging purpose.
+  This is a mayor change. 
+  Protocolversion is increased to 3 
+- Added support for --quiet                                (Joakim Axelsson)
+- Added support for --sorted                               (Joakim Axelsson)
+- Added support for --numeric                              (Joakim Axelsson)
+- Added support for --exact                                (Joakim Axelsson)
+- Added -Z (Zero) which zero's the counter                 (Joakim Axelsson)
+  on one or all pools.
+- Added support for --debug that prints all debug-messages (Joakim Axelsosn)
+  in userspace. Need to be compiled with 
+  IP_POOL_DEBUG tho.
+- Added null pooltype. For demostration and                (Joakim Axelsson)
+  pooltype skeleton mostly                                 
+- Fixed bug with possibly renaming to an already           (Joakim Axelsson) 
+  existing pool.
+- Change error to OTHER_PROBLEM on add and del IP.         (Joakim Axelsson)
+
+0.2.1-0.2.3
+- Better handling of references                            (Patrick Schaaf)
+- Various bugfixes                                         (Patrick Schaaf)
+- Cleaning up the code in kernelspace                      (Patrick Schaaf)
+
+0.2.0:
+- Rewrote the entrie system. Modulized it.                 (Joakim Axelsson)
diff --git a/ipset/Makefile b/ipset/Makefile
new file mode 100644 (file)
index 0000000..087a9dd
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/make
+
+######################################################################
+# YOU SHOULD NOT NEED TO TOUCH ANYTHING BELOW THIS LINE
+######################################################################
+
+ifndef KERNEL_DIR
+KERNEL_DIR=/usr/src/linux
+endif
+
+IPSET_VERSION:=2.2.8
+
+PREFIX:=/usr/local
+LIBDIR:=$(PREFIX)/lib
+BINDIR:=$(PREFIX)/sbin
+MANDIR:=$(PREFIX)/man
+INCDIR:=$(PREFIX)/include
+IPSET_LIB_DIR:=$(LIBDIR)/ipset
+
+# 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
+SH_CFLAGS:=$(CFLAGS) -fPIC
+SETTYPES:=ipmap portmap macipmap iphash nethash iptree ipporthash
+
+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)
+
+all: $(PROGRAMS) $(SHARED_LIBS)
+
+install: all $(INSTALL)
+
+clean: $(EXTRA_CLEANS)
+       rm -rf $(PROGRAMS) $(SHARED_LIBS) *.o *~
+
+#The ipset(8) self
+ipset.o: ipset.c
+       $(CC) $(CFLAGS) -DIPSET_VERSION=\"$(IPSET_VERSION)\" -DIPSET_LIB_DIR=\"$(IPSET_LIB_DIR)\" -c -o $@ $<
+
+ipset: ipset.o
+       $(CC) $(CFLAGS) -ldl -rdynamic -o $@ $^
+
+#Pooltypes
+ipset_%.o: ipset_%.c
+       $(CC) $(SH_CFLAGS) -o $@ -c $<
+
+libipset_%.so: ipset_%.o
+       $(LD) -shared -o $@ $<
+
+$(DESTDIR)$(LIBDIR)/ipset/libipset_%.so: libipset_%.so
+       @[ -d $(DESTDIR)$(LIBDIR)/ipset ] || mkdir -p $(DESTDIR)$(LIBDIR)/ipset
+       cp $< $@
+
+$(DESTDIR)$(BINDIR)/ipset: ipset
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+$(DESTDIR)$(MANDIR)/man8/ipset.8: ipset.8
+       @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
+       cp $< $@
diff --git a/ipset/TODO b/ipset/TODO
new file mode 100644 (file)
index 0000000..9d46233
--- /dev/null
@@ -0,0 +1,2 @@
+- rewrite kernel-userspace communication from sockopt to netlink
+- IPv6 support
diff --git a/ipset/ipset.8 b/ipset/ipset.8
new file mode 100644 (file)
index 0000000..89a86ce
--- /dev/null
@@ -0,0 +1,445 @@
+.TH IPSET 8 "Feb 05, 2004" "" ""
+.\"
+.\" Man page written by Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipset \- administration tool for IP sets
+.SH SYNOPSIS
+.BR "ipset -N " "set type-specification [options]"
+.br
+.BR "ipset -[XFLSHh] " "[set] [options]"
+.br
+.BR "ipset -[EW] " "from-set to-set"
+.br
+.BR "ipset -[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. 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
+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. In order to define a
+binding it is not required that the entry be already added to the set. 
+The sets may have a default binding, which is valid for every set element 
+for which there is no binding defined at all.
+.P
+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 single
+reference pointing to it.
+.SH OPTIONS
+The options that are recognized by
+.B ipset
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform.  Only one of them
+can be specified on the command line unless otherwise specified
+below.  For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B ipset
+can differentiate it from all other options.
+.TP
+.BI "-N, --create " "\fIsetname\fP type type-specific-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 two sets as they referenced in the Linux kernel.
+.B
+iptables
+rules or
+.B
+ipset
+bindings pointing to the content of from-setname will point to 
+the content of 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 (if the given set
+type supports the operation).
+.TP
+.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 a saved session generated by --save. The saved session
+can be fed from stdin.
+
+When generating a session file please note that the supported commands
+(create set, add element, bind) must appear in a strict order: first create
+the set, then add all elements. Then create the next set, add all its elements
+and so on. Finally you can list all binding commands. Also, it is a restore
+operation, so the sets being restored must not exist.
+.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
+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, 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
+possible errors.
+.SH SET TYPES
+ipset supports the following set types:
+.SS ipmap
+The ipmap set type uses a memory range, where each bit represents
+one IP address. An ipmap set can store up to 65536 (B-class network)
+IP addresses. The ipmap set type is very fast and memory cheap, great
+for use when one want to match certain IPs in a range. Using the
+.B "--netmask"
+option with a CIDR netmask value between 0-32 when creating an ipmap
+set, you will be able to store and match network addresses: i.e an
+IP address will be in the set if the value resulted by masking the address
+with the specified netmask can be found in the set.
+.P
+Options to use when creating an ipmap set:
+.TP
+.BR "--from " from-IP
+.TP
+.BR "--to " to-IP
+Create an ipmap set from the specified range.
+.TP
+.BR "--network " IP/mask
+Create an ipmap set from the specified network.
+.TP
+.BR "--netmask " CIDR-netmask
+When the optional
+.B "--netmask"
+parameter specified, network addresses will be 
+stored in the set instead of IP addresses, and the from-IP parameter
+must be a network address.
+.SS macipmap
+The macipmap set type uses a memory range, where each 8 bytes
+represents one IP and a MAC addresses. A macipmap set type can store
+up to 65536 (B-class network) IP addresses with MAC.
+When adding an entry to a macipmap set, you must specify the entry as
+.I IP%MAC.
+When deleting or testing macipmap entries, the
+.I %MAC
+part is not mandatory.
+.P
+Options to use when creating an macipmap set:
+.TP
+.BR "--from " from-IP
+.TP
+.BR "--to " to-IP
+Create a macipmap set from the specified range.
+.TP
+.BR "--network " IP/mask
+Create a macipmap set from the specified network.
+.TP
+.BR "--matchunset"
+When the optional
+.B "--matchunset"
+parameter specified, IP addresses which could be stored 
+in the set but not set yet, will always match.
+.P
+Please note, the 
+.I
+set
+and
+.I
+SET
+netfilter kernel modules
+.B
+always
+use the source MAC address from the packet to match, add or delete
+entries from a macipmap type of set.
+.SS portmap
+The portmap set type uses a memory range, where each bit represents
+one port. A portmap set type can store up to 65536 ports.
+The portmap set type is very fast and memory cheap.
+.P
+Options to use when creating an portmap set:
+.TP
+.BR "--from " from-port
+.TP
+.BR "--to " to-port
+Create a portmap set from the specified range.
+.SS iphash
+The iphash set type uses a 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
+great 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.
+.P
+Options to use when creating an iphash set:
+.TP
+.BR "--hashsize " hashsize
+The initial hash size (default 1024)
+.TP
+.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.
+.P
+Sets created by zero valued resize parameter won't be resized at all.
+The lookup time in an iphash type of set approximately linearly grows with
+the value of the 
+.B
+probes
+parameter. At the same time higher 
+.B
+probes
+values result a better utilized hash while smaller values 
+produce a larger, sparse hash.
+.SS nethash
+The nethash set type uses a hash to store different size of
+network addresses. The
+.I
+IP
+"address" used in the ipset commands must be in the form
+.I
+IP-address/cidr-size
+where the CIDR block size must be in the inclusive range of 1-31.
+In order to avoid clashes in the hash 
+double-hashing, and as a last resort, dynamic growing of the hash performed.
+.P
+Options to use when creating an nethash set:
+.TP
+.BR "--hashsize " hashsize
+The initial hash size (default 1024)
+.TP
+.BR "--probes " probes
+How many times try to resolve clashing at adding an IP to the hash 
+by double-hashing (default 4).
+.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
+.P
+An IP address will be in a nethash type of set if it is in any of the
+netblocks added to the set and the matching always start from the smallest
+size of netblock (most specific netmask) to the biggest ones (least
+specific netmasks). When adding/deleting IP addresses
+to a nethash set by the
+.I
+SET
+netfilter kernel module, it will be added/deleted by the smallest
+netblock size which can be found in the set.
+.P
+The lookup time in a nethash type of set is approximately linearly 
+grows with the times of the
+.B
+probes
+parameter and the number of different mask parameters in the hash.
+Otherwise the same speed and memory efficiency comments applies here 
+as at the iphash type.
+.SS ipporthash
+The ipporthash set type uses a hash to store IP address and port pairs.
+In order to avoid clashes in the hash double-hashing, and as a last
+resort, dynamic growing of the hash performed. An ipporthash set can 
+store up to 65536 (B-class network) IP addresses with all possible port
+values. When adding, deleting and testing values in an ipporthash type of
+set, the entries must be specified as
+.B
+"IP%port".
+.P
+The ipporthash types of sets evaluates two src/dst parameters of the 
+.I
+set
+match and 
+.I
+SET
+target. 
+.P
+Options to use when creating an ipporthash set:
+.TP
+.BR "--from " from-IP
+.TP
+.BR "--to " to-IP
+Create an ipporthash set from the specified range.
+.TP
+.BR "--network " IP/mask
+Create an ipporthash set from the specified network.
+.TP
+.BR "--hashsize " hashsize
+The initial hash size (default 1024)
+.TP
+.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.
+.P
+The same resizing, speed and memory efficiency comments applies here 
+as at the iphash type.
+.SS iptree
+The iptree set type uses a tree to store IP addresses, optionally 
+with timeout values.
+.P
+Options to use when creating an iptree set:
+.TP
+.BR "--timeout " value
+The timeout value for the entries in seconds (default 0)
+.P
+If a set was created with a nonzero valued 
+.B "--timeout"
+parameter then one may add IP addresses to the set with a specific 
+timeout value using the syntax 
+.I IP%timeout-value.
+.SH GENERAL RESTRICTIONS
+Setnames starting with colon (:) cannot be defined. Zero valued set 
+entries cannot be used with hash type of sets.
+.SH COMMENTS
+If you want to store same size subnets from a given network
+(say /24 blocks from a /8 network), use the ipmap set type.
+If you want to store random same size networks (say random /24 blocks), 
+use the iphash set type. If you have got random size of netblocks, 
+use nethash.
+.SH DIAGNOSTICS
+Various error messages are printed to standard error.  The exit code
+is 0 for correct functioning.  Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs? No, just funny features. :-)
+OK, just kidding...
+.SH SEE ALSO
+.BR iptables (8),
+.SH AUTHORS
+Jozsef Kadlecsik wrote ipset, which is based on ippool by
+Joakim Axelsson, Patrick Schaaf and Martin Josefsson.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
diff --git a/ipset/ipset.c b/ipset/ipset.c
new file mode 100644 (file)
index 0000000..adf37b1
--- /dev/null
@@ -0,0 +1,2209 @@
+/* Copyright 2000-2002 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ * 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 version 2 as 
+ * published by the Free Software Foundation.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <dlfcn.h>
+#include <asm/bitops.h>
+
+#include "ipset.h"
+
+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 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;
+
+#define TEMPFILE_PATTERN       "/ipsetXXXXXX"
+
+#ifdef IPSET_DEBUG
+int option_debug = 0;
+#endif
+
+#define OPTION_OFFSET 256
+static unsigned int global_option_offset = 0;
+
+/* 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', '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_BINDING            0x0010U         /* -b */
+#define NUMBER_OF_OPT 5
+static const char optflags[] =
+    { 'n', 's', 'q', 'z', 'b' };
+
+static struct option opts_long[] = {
+       /* set operations */
+       {"create",  1, 0, 'N'},
+       {"destroy", 2, 0, 'X'},
+       {"flush",   2, 0, 'F'},
+       {"rename",  1, 0, 'E'},
+       {"swap",    1, 0, 'W'},
+       {"list",    2, 0, 'L'},
+
+       {"save",    2, 0, 'S'},
+       {"restore", 0, 0, 'R'},
+
+       /* ip in set operations */
+       {"add",     1, 0, 'A'},
+       {"del",     1, 0, 'D'},
+       {"test",    1, 0, 'T'},
+       
+       /* binding operations */
+       {"bind",    1, 0, 'B'},
+       {"unbind",  1, 0, 'U'},
+       
+       /* free options */
+       {"numeric", 0, 0, 'n'},
+       {"sorted",  0, 0, 's'},
+       {"quiet",   0, 0, 'q'},
+       {"binding", 1, 0, 'b'},
+
+#ifdef IPSET_DEBUG
+       /* debug (if compiled with it) */
+       {"debug",   0, 0, 'z'},
+#endif
+
+       /* version and help */
+       {"version", 0, 0, 'V'},
+       {"help",    2, 0, 'H'},
+
+       /* end */
+       {0}
+};
+
+static char opts_short[] =
+    "-N:X::F::E:W:L::S::RA:D:T: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.
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = {
+       /*            -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,
+               "Try `%s -H' or '%s --help' for more information.\n",
+               program_name, program_name);
+       exit(status);
+}
+
+void exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       if (!option_quiet) {
+               va_start(args, msg);
+               fprintf(stderr, "%s v%s: ", program_name, program_version);
+               vfprintf(stderr, msg, args);
+               va_end(args);
+               fprintf(stderr, "\n");
+               if (line)
+                       fprintf(stderr, "Restore failed at line %u:\n", line);
+               if (status == PARAMETER_PROBLEM)
+                       exit_tryhelp(status);
+               if (status == VERSION_PROBLEM)
+                       fprintf(stderr,
+                               "Perhaps %s or your kernel needs to be upgraded.\n",
+                               program_name);
+       }
+
+       exit(status);
+}
+
+void ipset_printf(char *msg, ...)
+{
+       va_list args;
+
+       if (!option_quiet) {
+               va_start(args, msg);
+               vfprintf(stdout, msg, args);
+               va_end(args);
+               fprintf(stdout, "\n");
+       }
+}
+
+static void generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0;      /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 1; j <= NUMBER_OF_CMD; j++) {
+                       if (command != j)
+                               continue;
+
+                       if (!(options & (1 << 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-1][i] != 'x')
+                                       legal = 1;
+                               else if (legal == 0)
+                                       legal = -1;
+                       }
+               }
+               if (legal == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Illegal option `-%c' with this command\n",
+                                  optflags[i]);
+       }
+}
+
+static char opt2char(int option)
+{
+       const char *ptr;
+       for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static char cmd2char(int option)
+{
+       if (option <= CMD_NONE || option > NUMBER_OF_CMD)
+               return ' '; 
+
+       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(*cmd), cmd2char(newcmd));
+       *cmd = newcmd;
+}
+
+static void add_option(unsigned int *options, unsigned int option)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+}
+
+void *ipset_malloc(size_t size)
+{
+       void *p;
+
+       if (size == 0)
+               return NULL;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("ipset: not enough memory");
+               exit(1);
+       }
+       return p;
+}
+
+char *ipset_strdup(const char *s)
+{
+       char *p;
+
+       if ((p = strdup(s)) == NULL) {
+               perror("ipset: not enough memory");
+               exit(1);
+       }
+       return p;
+}
+
+void ipset_free(void **data)
+{
+       if (*data == NULL)
+               return;
+
+       free(*data);
+       *data = NULL;
+}
+
+static struct option *merge_options(struct option *oldopts,
+                                   const struct option *newopts,
+                                   unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = ipset_malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+static char *ip_tohost(const struct in_addr *addr)
+{
+       struct hostent *host;
+
+       if ((host = gethostbyaddr((char *) addr,
+                                 sizeof(struct in_addr),
+                                 AF_INET)) != NULL) {
+               DP("%s", host->h_name);
+               return (char *) host->h_name;
+       }
+
+       return (char *) NULL;
+}
+
+static char *ip_tonetwork(const struct in_addr *addr)
+{
+       struct netent *net;
+
+       if ((net = getnetbyaddr((long) ntohl(addr->s_addr), 
+                               AF_INET)) != NULL) {
+               DP("%s", net->n_name);
+               return (char *) net->n_name;
+       }
+
+       return (char *) NULL;
+}
+
+/* Return a string representation of an IP address.
+ * Please notice that a pointer to static char* area is returned.
+ */
+char *ip_tostring(ip_set_ip_t ip, unsigned options)
+{
+       struct in_addr addr;
+       addr.s_addr = htonl(ip);
+
+       if (!(options & OPT_NUMERIC)) {
+               char *name;
+               if ((name = ip_tohost(&addr)) != NULL ||
+                   (name = ip_tonetwork(&addr)) != NULL)
+                       return name;
+       }
+       
+       return inet_ntoa(addr);
+}
+
+char *binding_ip_tostring(struct set *set, ip_set_ip_t ip, unsigned options)
+{
+       return ip_tostring(ip, options);
+}
+char *ip_tostring_numeric(ip_set_ip_t ip)
+{
+       return ip_tostring(ip, OPT_NUMERIC);
+}
+
+/* Fills the 'ip' with the parsed ip or host in host byte order */
+void parse_ip(const char *str, ip_set_ip_t * ip)
+{
+       struct hostent *host;
+       struct in_addr addr;
+
+       DP("%s", str);
+       
+       if (inet_aton(str, &addr) != 0) {
+               *ip = ntohl(addr.s_addr);       /* We want host byte order */
+               return;
+       }
+
+       host = gethostbyname(str);
+       if (host != NULL) {
+               if (host->h_addrtype != AF_INET ||
+                   host->h_length != sizeof(struct in_addr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "host/network `%s' not an internet name",
+                                  str);
+               if (host->h_addr_list[1] != 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "host/network `%s' resolves to serveral ip-addresses. "
+                                  "Please specify one.", str);
+
+               *ip = ntohl(((struct in_addr *) host->h_addr_list[0])->s_addr);
+               return;
+       }
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", str);
+}
+
+/* Fills 'mask' with the parsed mask in host byte order */
+void parse_mask(const char *str, ip_set_ip_t * mask)
+{
+       struct in_addr addr;
+       unsigned int bits;
+
+       DP("%s", str);
+
+       if (str == NULL) {
+               /* no mask at all defaults to 32 bits */
+               *mask = 0xFFFFFFFF;
+               return;
+       }
+       if (strchr(str, '.') && inet_aton(str, &addr) != 0) {
+               *mask = ntohl(addr.s_addr);     /* We want host byte order */
+               return;
+       }
+       if (sscanf(str, "%d", &bits) != 1 || bits < 0 || bits > 32)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", str);
+
+       DP("bits: %d", bits);
+
+       *mask = bits != 0 ? 0xFFFFFFFF << (32 - bits) : 0L;
+}
+
+/* Combines parse_ip and parse_mask */
+void
+parse_ipandmask(const char *str, ip_set_ip_t * ip, ip_set_ip_t * mask)
+{
+       char buf[256];
+       char *p;
+
+       strncpy(buf, str, sizeof(buf) - 1);
+       buf[255] = '\0';
+
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               parse_mask(p + 1, mask);
+       } else
+               parse_mask(NULL, mask);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (*mask == 0U)
+               *ip = 0U;
+       else
+               parse_ip(buf, ip);
+
+       DP("%s ip: %08X (%s) mask: %08X",
+          str, *ip, ip_tostring_numeric(*ip), *mask);
+
+       /* Apply the netmask */
+       *ip &= *mask;
+
+       DP("%s ip: %08X (%s) mask: %08X",
+          str, *ip, ip_tostring_numeric(*ip), *mask);
+}
+
+/* Return a string representation of a port
+ * Please notice that a pointer to static char* area is returned
+ * and we assume TCP protocol.
+ */
+char *port_tostring(ip_set_ip_t port, unsigned options)
+{
+       struct servent *service;
+       static char name[] = "65535";
+       
+       if (!(options & OPT_NUMERIC)) {
+               if ((service = getservbyport(htons(port), "tcp")))
+                       return service->s_name;
+       }
+       sprintf(name, "%u", port);
+       return name;
+}
+
+int
+string_to_number(const char *str, unsigned int min, unsigned int max,
+                ip_set_ip_t *port)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(str, &end, 0);
+       if (*end == '\0' && end != str) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *port = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static int
+string_to_port(const char *str, ip_set_ip_t *port)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(str, "tcp")) != NULL) {
+               *port = ntohs((unsigned short) service->s_port);
+               return 0;
+       }
+       
+       return -1;
+}
+
+/* Fills the 'ip' with the parsed port in host byte order */
+void parse_port(const char *str, ip_set_ip_t *port)
+{      
+       if ((string_to_number(str, 0, 65535, port) != 0)
+             && (string_to_port(str, port) != 0))
+               exit_error(PARAMETER_PROBLEM, 
+                          "Invalid TCP port `%s' specified", str);     
+}
+
+/* 
+ * Settype functions
+ */
+static struct settype *settype_find(const char *typename)
+{
+       struct settype *runner = all_settypes;
+
+       DP("%s", typename);
+
+       while (runner != NULL) {
+               if (strncmp(runner->typename, typename, 
+                           IP_SET_MAXNAMELEN) == 0)
+                       return runner;
+
+               runner = runner->next;
+       }
+
+       return NULL;            /* not found */
+}
+
+static struct settype *settype_load(const char *typename)
+{
+       char path[sizeof(IPSET_LIB_DIR) + sizeof(IPSET_LIB_NAME) +
+                 strlen(typename)];
+       struct settype *settype;
+
+       /* do some search in list */
+       settype = settype_find(typename);
+       if (settype != NULL)
+               return settype; /* found */
+
+       /* Else we have to load it */
+       sprintf(path, IPSET_LIB_DIR IPSET_LIB_NAME, typename);
+
+       if (dlopen(path, RTLD_NOW)) {
+               /* Found library. */
+
+               settype = settype_find(typename);
+
+               if (settype != NULL)
+                       return settype;
+       }
+
+       /* Can't load the settype */
+       exit_error(PARAMETER_PROBLEM,
+                  "Couldn't load settype `%s':%s\n",
+                  typename, dlerror());
+
+       return NULL;            /* Never executed, but keep compilers happy */
+}
+
+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("%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 registered!\n",
+                          settype->typename);
+
+       /* Check version */
+       if (settype->protocol_version != IP_SET_PROTOCOL_VERSION)
+               exit_error(OTHER_PROBLEM,
+                          "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;
+       all_settypes = settype;
+
+       DP("%s registered", settype->typename);
+}
+
+/* Find set functions */
+static struct set *set_find_byid(ip_set_id_t id)
+{
+       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 struct set *set_find_byname(const char *name)
+{
+       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 ip_set_id_t set_find_free_index(const char *name)
+{
+       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);
+
+       return index;
+}
+
+/* 
+ * Send create set order to kernel
+ */
+static void set_create(const char *name, struct settype *settype)
+{
+       struct ip_set_req_create req_create;
+       size_t size;
+       void *data;
+
+       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);
+
+       /* 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);
+
+       kernel_sendto(CMD_CREATE, data, size);
+       free(data);
+}
+
+static void set_restore_create(const char *name, struct settype *settype)
+{
+       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);
+
+       /* 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);
+
+       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;
+}
+
+/*
+ * 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_std req;
+
+       DP("%s %s", cmd == CMD_DESTROY ? "destroy" : "flush", name);
+
+       req.op = op;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req.name, name);
+
+       kernel_sendto(cmd, &req, sizeof(struct ip_set_req_std));
+}
+
+/*
+ * Send rename/swap order to kernel
+ */
+static void set_rename(const char *name, const char *newname,
+                      unsigned op, unsigned cmd)
+{
+       struct ip_set_req_create req;
+
+       DP("%s %s %s", cmd == CMD_RENAME ? "rename" : "swap",
+                      name, newname);
+
+       req.op = op;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req.name, name);
+       strcpy(req.typename, newname);
+
+       kernel_sendto(cmd, &req,
+                     sizeof(struct ip_set_req_create));
+}
+
+/*
+ * 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 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;
+
+       res = kernel_getfrom_handleerrno(cmd, data, &size);
+
+       if (res != 0 || size != req_size) {
+               free(data);
+               if (repeated++ < LIST_TRIES)
+                       goto tryagain;
+               exit_error(OTHER_PROBLEM,
+                          "Tried to get sets from kernel %d times"
+                          " and failed. Please try again when the load on"
+                          " the sets has gone down.", LIST_TRIES);
+       }
+               
+       /* 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->id = name_list->id;
+               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;
+}
+
+/*
+ * 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;
+
+       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.");
+
+       set = set_find_byid(hash->id);
+       if (!(set && set_list[hash->binding]))
+               exit_error(OTHER_PROBLEM,
+                          "Save binding failed, try again later.");
+       printf("-B %s %s -b %s\n",
+               set->name,
+               set->settype->bindip_tostring(set, 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.");
+
+       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;
+
+       /* Init set header */
+       used = sizeof(struct ip_set_save);
+       settype->initheader(set, data + offset + used);
+
+       /* Print create set */
+       settype->saveheader(set, OPT_NUMERIC);
+
+       /* Print add IPs */
+       used += set_save->header_size;
+       settype->saveips(set, data + offset + used,
+                        set_save->members_size, OPT_NUMERIC);
+
+       return (used + set_save->members_size);
+}
+
+static size_t save_default_bindings(void *data, int *bindings)
+{
+       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);
+       }
+
+       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.");
+
+               printf("-B %s %s -b %s\n",
+                       set->name, IPSET_TOKEN_DEFAULT, 
+                       set_list[set_save->binding]->name);
+       }
+       return (sizeof(struct ip_set_save)
+               + set_save->header_size
+               + set_save->members_size);
+}
+
+static int try_save_sets(const char name[IP_SET_MAXNAMELEN])
+{
+       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);
+       
+       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);
+
+               if (res != 0 || size != req_size) {
+                       free(data);
+                       return -EAGAIN;
+               }
+       }
+
+       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);
+       }
+       /* 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;
+}
+
+/*
+ * Performs a save to stdout
+ */
+static void set_save(const char name[IP_SET_MAXNAMELEN])
+{
+       int i;
+
+       DP("%s", name);
+       for (i = 0; i < LIST_TRIES; i++)
+               if (try_save_sets(name) == 0)
+                       return;
+
+       if (errno == EAGAIN)
+               exit_error(OTHER_PROBLEM,
+                          "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);
+}
+
+/*
+ * Restore operation
+ */
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc = 0;
+
+/* Build faked argv from parsed line */
+static void build_argv(int line, char *buffer) {
+       char *ptr;
+       int i;
+
+       /* Reset */     
+       for (i = 1; i < newargc; i++)
+               free(newargv[i]);
+       newargc = 1;
+
+       ptr = strtok(buffer, " \t\n");
+       newargv[newargc++] = ipset_strdup(ptr);
+       while ((ptr = strtok(NULL, " \t\n")) != NULL) {
+               if ((newargc + 1) < sizeof(newargv)/sizeof(char *))
+                       newargv[newargc++] = ipset_strdup(ptr);
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Line %d is too long to restore\n", line);
+       }
+}
+
+static FILE *create_tempfile(void)
+{
+       char buffer[1024];      
+       char *tmpdir = NULL;
+       char *filename;
+       int fd;
+       FILE *file;
+       
+       if (!(tmpdir = getenv("TMPDIR")) && !(tmpdir = getenv("TMP")))
+               tmpdir = "/tmp";
+       filename = ipset_malloc(strlen(tmpdir) + strlen(TEMPFILE_PATTERN) + 1);
+       strcpy(filename, tmpdir);
+       strcat(filename, TEMPFILE_PATTERN);
+       
+       (void) umask(077);      /* Create with restrictive permissions */
+       fd = mkstemp(filename);
+       if (fd == -1)
+               exit_error(OTHER_PROBLEM, "Could not create temporary file.");
+       if (!(file = fdopen(fd, "r+")))
+               exit_error(OTHER_PROBLEM, "Could not open temporary file.");
+       if (unlink(filename) == -1)
+               exit_error(OTHER_PROBLEM, "Could not unlink temporary file.");
+       free(filename);
+
+       while (fgets(buffer, sizeof(buffer), stdin)) {
+               fputs(buffer, file);
+       }
+       fseek(file, 0L, SEEK_SET);
+
+       return file;
+}
+
+/*
+ * Performs a restore from a file
+ */
+static void set_restore(char *argv0)
+{
+       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;
+       FILE *in;
+       int res;
+       
+       /* Create and store stdin in temporary file */
+       in = create_tempfile();
+       
+       /* 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;
+               }
+                       
+               /* -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);
+               }
+               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 (bindings)
+                               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; 
+               }
+               case 'A': {
+                       if (name == NULL
+                           || 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 (bindings)
+                               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 */
+       fseek(in, 0L, SEEK_SET);
+       first_pass = line;
+       line = 0;
+       
+       /* Initialize newargv/newargc */
+       newargv[newargc++] = ipset_strdup(argv0);
+       
+       /* Second pass: build up restore request */
+       while (fgets(buffer, sizeof(buffer), in)) {             
+               line++;
+
+               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);
+
+       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);
+       }
+}
+
+/*
+ * Send ADT_GET order to kernel for a set
+ */
+static struct set *set_adt_get(const char *name)
+{
+       struct ip_set_req_adt_get req_adt_get;
+       struct set *set;
+       size_t size;
+
+       DP("%s", name);
+
+       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);
+
+       kernel_getfrom(CMD_ADT_GET, &req_adt_get, &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);
+
+       return set;
+}      
+
+/*
+ * Send add/del/test order to kernel for a set
+ */
+static int set_adtip(struct set *set, const char *adt, 
+                    unsigned op, unsigned cmd)
+{
+       struct ip_set_req_adt *req_adt;
+       size_t size;
+       void *data;
+       int res = 0;
+
+       DP("%s -> %s", set->name, adt);
+
+       /* 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);
+
+       /* 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);
+       
+       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 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;
+               }
+       else
+               switch (op) {
+               case IP_SET_OP_TEST_IP:
+                       ipset_printf("%s is NOT in set %s.", adt, set->name);
+                       res = 1;
+                       break;
+               default:
+                       break;
+               }
+       free(data);
+
+       return res;
+}
+
+static void set_restore_add(struct set *set, const char *adt)
+{
+       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;
+}
+
+/*
+ * Send bind/unbind/test binding order to kernel for a set
+ */
+static int set_bind(struct set *set, const char *adt,
+                   const char *binding,
+                   unsigned op, unsigned cmd)
+{
+       struct ip_set_req_bind *req_bind;
+       size_t size;
+       void *data;
+       int res = 0;
+
+       /* set may be null: '-U :all: :all:|:default:' */
+       DP("(%s, %s) -> %s", set ? set->name : IPSET_TOKEN_ALL, adt, binding);
+
+       /* Alloc memory for the data to send */
+       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);
+
+       /* 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);
+       }
+
+       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);
+
+       return res;
+}
+
+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;
+       }
+       /* 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
+               set->settype->bindip_parse(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);
+}
+
+/*
+ * Print operation
+ */
+
+static void print_bindings(struct set *set,
+                          void *data, size_t size, unsigned options,
+                          char * (*printip)(struct set *set, 
+                                            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(set, hash->ip, options),
+                       set_list[hash->binding]->name);
+               offset += sizeof(struct ip_set_hash_list);
+       }
+}
+
+/* Help function to set_list() */
+static size_t print_set(void *data, unsigned options)
+{
+       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;
+
+       /* 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);
+
+       /* Init header */
+       offset = sizeof(struct ip_set_list);
+       settype->initheader(set, data + offset);
+
+       /* Pretty print the type header */
+       printf("Header:");
+       settype->printheader(set, options);
+
+       /* 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);
+
+       /* Print bindings */
+       printf("Bindings:\n");
+       offset += setlist->members_size;
+       print_bindings(set,
+                      data + offset, setlist->bindings_size, options,
+                      settype->bindip_tostring);
+
+       printf("\n");           /* One newline between sets */
+       
+       return (offset + setlist->bindings_size);
+}
+
+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("%s", name);
+       /* Load set_list from kernel */
+       size = req_size = load_set_list(name, &index,
+                                       IP_SET_OP_LIST_SIZE, CMD_LIST);
+
+       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;
+       }
+       while (size != req_size)
+               size += print_set(data + size, options);
+
+       ipset_free(&data);
+       return res;
+}
+
+/* Print a set or all sets
+ * All sets: name = NULL
+ */
+static void list_sets(const char name[IP_SET_MAXNAMELEN], unsigned options)
+{
+       int i;
+
+       DP("%s", name);
+       for (i = 0; i < LIST_TRIES; i++)
+               if (try_list_sets(name, options) == 0)
+                       return;
+
+       if (errno == EAGAIN)
+               exit_error(OTHER_PROBLEM,
+                          "Tried to list sets from kernel %d times"
+                          " and failed. Please try again when the load on"
+                          " the sets has gone down.", LIST_TRIES);
+       else
+               kernel_error(CMD_LIST, errno);
+}
+
+/* Prints help
+ * If settype is non null help for that type is printed as well
+ */
+static void set_help(const struct settype *settype)
+{
+#ifdef IPSET_DEBUG
+       char debughelp[] =
+              "  --debug      -z   Enable debugging\n\n";
+#else
+       char debughelp[] = "\n";
+#endif
+
+       printf("%s v%s\n\n"
+              "Usage: %s -N new-set settype [options]\n"
+              "       %s -[XFLSH] [set] [options]\n"
+              "       %s -[EW] from-set to-set\n"
+              "       %s -[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);
+
+       printf("Commands:\n"
+              "Either long or short options are allowed.\n"
+              "  --create  -N setname settype <options>\n"
+              "                    Create a new set\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] [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 [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"
+              "Options:\n"
+              "  --sorted     -s   Numeric sort of the IPs in -L\n"
+              "  --numeric    -n   Numeric output of addresses in a -L\n"
+              "  --quiet      -q   Suppress any output to stdout and stderr.\n"
+              "  --binding    -b   Specifies the binding for -B\n");
+       printf(debughelp);
+
+       if (settype != NULL) {
+               printf("Type '%s' specific:\n", settype->typename);
+               settype->usage();
+       }
+}
+
+static int find_cmd(const char option)
+{
+       int i;
+       
+       for (i = 1; i <= NUMBER_OF_CMD; i++)
+               if (cmdflags[i] == option)
+                       return i;
+                       
+       return CMD_NONE;
+}
+
+static int parse_adt_cmdline(unsigned command,
+                            const char *name,
+                            char *adt,
+                            struct set **set,
+                            struct settype **settype)
+{
+       int res = 0;
+
+       /* -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 res = 0;
+       unsigned command = CMD_NONE;
+       unsigned options = 0;
+       int c;
+       
+       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
+                        */
+               case 'h':
+               case 'H':{      /* Help: -H [typename [options]] */
+                               check_protocolversion();
+                               set_command(&command, CMD_HELP);
+                               
+                               if (optarg)
+                                       settype = check_set_typename(optarg);
+                               else if (optind < argc
+                                        && argv[optind][0] != '-')
+                                       settype = check_set_typename(argv[optind++]);
+                               
+                               break;
+                       }
+
+               case 'V':{      /* Version */
+                               printf("%s v%s Protocol version %u.\n",
+                                      program_name, program_version,
+                                      IP_SET_PROTOCOL_VERSION);
+                               check_protocolversion();
+                               exit(0);
+                       }
+
+               case 'N':{      /* Create: -N name typename options */
+                               set_command(&command, CMD_CREATE);
+
+                               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,
+                                            settype->create_opts,
+                                            &settype->option_offset);
+
+                               /* Reset space for create data */
+                               memset(settype->data, 0, settype->create_size);
+
+                               /* Zero the flags */
+                               settype->flags = 0;
+
+                               DP("call create_init");
+                               /* Call the settype create_init */
+                               settype->create_init(settype->data);
+
+                               break;
+                       }
+
+               case 'X':       /* Destroy */
+               case 'F':       /* Flush */
+               case 'L':       /* List */
+               case 'S':{      /* Save */
+                               set_command(&command, find_cmd(c));
+
+                               if (optarg)
+                                       name = check_set_name(optarg);
+                               else if (optind < argc
+                                          && argv[optind][0] != '-')
+                                       name = check_set_name(argv[optind++]);
+                               else
+                                       name = all_sets;
+
+                               break;
+                       }
+
+               case 'R':{      /* Restore */
+                               set_command(&command, find_cmd(c));
+
+                               break;
+                       }
+
+               case 'E':       /* Rename */
+               case 'W':{      /* Swap */
+                               set_command(&command, find_cmd(c));
+                               name = check_set_name(optarg);
+
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       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));
+
+                               break;
+                       }
+
+               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));
+
+                               name = check_set_name(optarg);
+
+                               /* IP */
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       adt = argv[optind++];
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires setname and IP",
+                                                  c);
+
+                               res = parse_adt_cmdline(command, name, adt,
+                                                       &set, &settype);
+
+                               if (!res)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind - 1]);
+
+                               break;
+                       }
+
+                       /* options */
+
+               case 'n':
+                       add_option(&options, OPT_NUMERIC);
+                       break;
+
+               case 's':
+                       add_option(&options, OPT_SORTED);
+                       break;
+
+               case 'q':
+                       add_option(&options, OPT_QUIET);
+                       option_quiet = 1;
+                       break;
+
+#ifdef IPSET_DEBUG
+               case 'z':       /* debug */
+                       add_option(&options, OPT_DEBUG);
+                       option_debug = 1;
+                       break;
+#endif
+
+               case 'b':
+                       add_option(&options, OPT_BINDING);
+                       binding = check_set_name(optarg);
+                       break;
+
+               case 1: /* non option */
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+                       break;  /*always good */
+
+               default:{
+                               DP("default");
+
+                               switch (command) {
+                               case CMD_CREATE:
+                                       res = settype->create_parse(
+                                                       c - settype->option_offset,
+                                                       argv,
+                                                       settype->data,
+                                                       &settype->flags);
+                                       break;
+
+                               default:
+                                       res = 0;        /* failed */
+                               }       /* switch (command) */
+
+
+                               if (!res)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind - 1]);
+
+                       }
+
+                       DP("next arg");
+               }       /* switch */
+
+       }       /* while( getopt_long() ) */
+
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (command == CMD_NONE)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+
+       /* Check options */
+       generic_opt_check(command, options);
+
+       DP("cmd: %c", cmd2char(command));
+
+       switch (command) {
+       case CMD_CREATE:
+               DP("CMD_CREATE");
+               if (restore)
+                       set_restore_create(name, settype);
+               else
+                       set_create(name, settype);
+               break;
+
+       case CMD_DESTROY:
+               set_destroy(name, IP_SET_OP_DESTROY, CMD_DESTROY);
+               break;
+
+       case CMD_FLUSH:
+               set_destroy(name, IP_SET_OP_FLUSH, CMD_FLUSH);
+               break;
+
+       case CMD_RENAME:
+               set_rename(name, newname, IP_SET_OP_RENAME, CMD_RENAME);
+               break;
+
+       case CMD_SWAP:
+               set_rename(name, newname, IP_SET_OP_SWAP, CMD_SWAP);
+               break;
+
+       case CMD_LIST:
+               list_sets(name, options);
+               break;
+
+       case CMD_SAVE:
+               set_save(name);
+               break;
+
+       case CMD_RESTORE:
+               set_restore(argv[0]);
+               break;
+
+       case CMD_ADD:
+               if (restore)
+                       set_restore_add(set, adt);
+               else
+                       set_adtip(set, adt, IP_SET_OP_ADD_IP, CMD_ADD);
+               break;
+
+       case CMD_DEL:
+               set_adtip(set, adt, IP_SET_OP_DEL_IP, CMD_DEL);
+               break;
+
+       case CMD_TEST:
+               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_BIND:
+               if (restore)
+                       set_restore_bind(set, adt, binding);
+               else
+                       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 */
+               break; /* Keep the compiler happy */
+
+       }       /* switch( command ) */
+
+       return res;
+}
+
+
+int main(int argc, char *argv[])
+{      
+       return parse_commandline(argc, argv);
+
+}
diff --git a/ipset/ipset.h b/ipset/ipset.h
new file mode 100644 (file)
index 0000000..50a3476
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef __IPSET_H
+#define __IPSET_H
+
+/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ *                     Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <getopt.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+#include <linux/netfilter_ipv4/ip_set.h>
+
+#define IPSET_LIB_NAME "/libipset_%s.so"
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+
+#define LIST_TRIES 5
+
+#ifdef IPSET_DEBUG
+extern int option_debug;
+#define DP(format, args...) if (option_debug)                  \
+       do {                                                    \
+               fprintf(stderr, "%s: %s (DBG): ", __FILE__, __FUNCTION__);\
+               fprintf(stderr, format "\n" , ## args);                 \
+       } while (0)
+#else
+#define DP(format, args...)
+#endif
+
+/* 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
+};
+
+/* The view of an ipset in userspace */
+struct set {
+       char name[IP_SET_MAXNAMELEN];           /* Name of the set */
+       ip_set_id_t id;                         /* Unique set id */
+       ip_set_id_t index;                      /* Array index */
+       unsigned ref;                           /* References in kernel */
+       struct settype *settype;                /* Pointer to set type functions */
+};
+
+struct settype {
+       struct settype *next;
+
+       char typename[IP_SET_MAXNAMELEN];
+
+       int protocol_version;
+
+       /*
+        * Create set
+        */
+
+       /* Size of create data. Will be sent to kernel */
+       size_t create_size;
+
+       /* Initialize the create. */
+       void (*create_init) (void *data);
+
+       /* Function which parses command options; returns true if it ate an option */
+       int (*create_parse) (int c, char *argv[], void *data,
+                            unsigned *flags);
+
+       /* Final check; exit if not ok. */
+       void (*create_final) (void *data, unsigned int flags);
+
+       /* Pointer to list of extra command-line options for create */
+       struct option *create_opts;
+
+       /*
+        * Add/del/test IP
+        */
+
+       /* Size of data. Will be sent to kernel */
+       size_t adt_size;
+
+       /* Function which parses command options */
+       ip_set_ip_t (*adt_parser) (unsigned cmd, const char *optarg, void *data);
+
+       /*
+        * Printing
+        */
+
+       /* Size of header. */
+       size_t header_size;
+
+       /* Initialize the type-header */
+       void (*initheader) (struct set *set, const void *data);
+
+       /* Pretty print the type-header */
+       void (*printheader) (struct set *set, unsigned options);
+
+       /* Pretty print all IPs */
+       void (*printips) (struct set *set, void *data, size_t len, unsigned options);
+
+       /* Pretty print all IPs sorted */
+       void (*printips_sorted) (struct set *set, void *data, size_t len, unsigned options);
+
+       /* Print save arguments for creating the set */
+       void (*saveheader) (struct set *set, unsigned options);
+
+       /* Print save for all IPs */
+       void (*saveips) (struct set *set, void *data, size_t len, unsigned options);
+
+       /* Conver a single IP (binding) to string */
+       char * (*bindip_tostring)(struct set *set, ip_set_ip_t ip, unsigned options);
+       
+       /* Parse an IP at restoring bindings. FIXME */
+       void (*bindip_parse) (const char *str, ip_set_ip_t * ip);
+
+       /* Print usage */
+       void (*usage) (void);
+
+       /* Internal data */
+       void *header;
+       void *data;
+       unsigned int option_offset;
+       unsigned int flags;
+};
+
+extern void settype_register(struct settype *settype);
+
+/* extern void unregister_settype(set_type_t *set_type); */
+
+extern void exit_error(enum exittype status, char *msg, ...);
+
+extern char *binding_ip_tostring(struct set *set,
+                                ip_set_ip_t ip, unsigned options);
+extern char *ip_tostring(ip_set_ip_t ip, unsigned options);
+extern char *ip_tostring_numeric(ip_set_ip_t ip);
+extern void parse_ip(const char *str, ip_set_ip_t * ip);
+extern void parse_mask(const char *str, ip_set_ip_t * mask);
+extern void parse_ipandmask(const char *str, ip_set_ip_t * ip,
+                           ip_set_ip_t * mask);
+extern char *port_tostring(ip_set_ip_t port, unsigned options);
+extern void parse_port(const char *str, ip_set_ip_t * port);
+extern int string_to_number(const char *str, unsigned int min, unsigned int max,
+                           ip_set_ip_t *port);
+
+extern void *ipset_malloc(size_t size);
+extern char *ipset_strdup(const char *);
+extern void ipset_free(void **data);
+
+#endif /* __IPSET_H */
diff --git a/ipset/ipset_iphash.c b/ipset/ipset_iphash.c
new file mode 100644 (file)
index 0000000..3272e6e
--- /dev/null
@@ -0,0 +1,297 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+#include <asm/types.h>
+
+#include <linux/netfilter_ipv4/ip_set_iphash.h>
+#include <linux/netfilter_ipv4/ip_set_jhash.h>
+
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_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)
+{
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+
+       DP("create INIT");
+
+       /* Default create parameters */ 
+       mydata->hashsize = 1024;
+       mydata->probes = 8;
+       mydata->resize = 50;
+       
+       mydata->netmask = 0xFFFFFFFF;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+       unsigned int bits;
+       ip_set_ip_t value;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+
+               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;
+
+       case '2':
+
+               if (string_to_number(optarg, 1, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid probes `%s' specified", optarg);
+
+               mydata->probes = value;
+               *flags |= OPT_CREATE_PROBES;
+
+               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);
+               
+               if (bits != 0)
+                       mydata->netmask = 0xFFFFFFFF << (32 - bits);
+
+               *flags |= OPT_CREATE_NETMASK;
+
+               DP("--netmask %x", mydata->netmask);
+               
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+#ifdef IPSET_DEBUG
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+
+       DP("hashsize %u probes %u resize %u",
+          mydata->hashsize, mydata->probes, mydata->resize);
+#endif
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"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(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_iphash *mydata =
+           (struct ip_set_req_iphash *) data;
+
+       parse_ip(optarg, &mydata->ip);
+       if (!mydata->ip)
+               exit_error(PARAMETER_PROBLEM,
+                          "Zero valued IP address `%s' specified", optarg);
+
+       return mydata->ip;      
+};
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_iphash_create *header =
+           (struct ip_set_req_iphash_create *) data;
+       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
+mask_to_bits(ip_set_ip_t mask)
+{
+       unsigned int bits = 32;
+       ip_set_ip_t maskaddr;
+       
+       if (mask == 0xFFFFFFFF)
+               return bits;
+       
+       maskaddr = 0xFFFFFFFE;
+       while (--bits >= 0 && maskaddr != mask)
+               maskaddr <<= 1;
+       
+       return bits;
+}
+       
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) set->settype->header;
+
+       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(struct set *set, void *data, size_t len, unsigned 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(struct set *set, unsigned options)
+{
+       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(struct set *set, void *data, size_t len, unsigned options)
+{
+       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 [--hashsize hashsize] [--probes probes ]\n"
+            "              [--resize resize] [--netmask CIDR-netmask]\n"
+            "-A set IP\n"
+            "-D set IP\n"
+            "-T set IP\n");
+}
+
+static struct settype settype_iphash = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_iphash_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_iphash),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_iphash),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips,          /* We only have the unsorted version */
+       .printips_sorted = &printips,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse = &parse_ip,
+       
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_iphash);
+
+}
diff --git a/ipset/ipset_ipmap.c b/ipset/ipset_ipmap.c
new file mode 100644 (file)
index 0000000..2d1c81c
--- /dev/null
@@ -0,0 +1,360 @@
+/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ *                     Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+
+#include <linux/netfilter_ipv4/ip_set_ipmap.h>
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+#define OPT_CREATE_NETWORK 0x04U
+#define OPT_CREATE_NETMASK 0x08U
+
+#define OPT_ADDDEL_IP      0x01U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+
+       DP("create INIT");
+       mydata->netmask = 0xFFFFFFFF;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+       unsigned int bits;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_ip(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  ip_tostring_numeric(mydata->from));
+
+               break;
+
+       case '2':
+               parse_ip(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '3':
+               parse_ipandmask(optarg, &mydata->from, &mydata->to);
+
+               /* Make to the last of from + mask */
+               if (mydata->to)
+                       mydata->to = mydata->from | ~(mydata->to);
+               else {
+                       mydata->from = 0x00000000;
+                       mydata->to = 0xFFFFFFFF;
+               }
+               *flags |= OPT_CREATE_NETWORK;
+
+               DP("--network from %x (%s)", 
+                  mydata->from, ip_tostring_numeric(mydata->from));
+               DP("--network to %x (%s)", 
+                  mydata->to, ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '4':
+               if (string_to_number(optarg, 0, 32, &bits))
+                       exit_error(PARAMETER_PROBLEM, 
+                                 "Invalid netmask `%s' specified", optarg);
+               
+               if (bits != 0)
+                       mydata->netmask = 0xFFFFFFFF << (32 - bits);
+
+               *flags |= OPT_CREATE_NETMASK;
+
+               DP("--netmask %x", mydata->netmask);
+               
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+#define ERRSTRLEN      256
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+       ip_set_ip_t range;
+       char errstr[ERRSTRLEN];
+
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to, or --network\n");
+
+       if (flags & OPT_CREATE_NETWORK) {
+               /* --network */
+               if ((flags & OPT_CREATE_FROM) || (flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --from or --to with --network\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+       DP("from : %x to: %x diff: %x", 
+          mydata->from, mydata->to,
+          mydata->to - mydata->from);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n");
+
+       if (flags & OPT_CREATE_NETMASK) {
+               unsigned int mask_bits, netmask_bits;
+               ip_set_ip_t mask;
+               
+               if ((mydata->from & mydata->netmask) != mydata->from)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%s is not a network address according to netmask %d\n",
+                                  ip_tostring_numeric(mydata->from),
+                                  mask_to_bits(mydata->netmask));
+               
+               mask = range_to_mask(mydata->from, mydata->to, &mask_bits);
+               if (!mask
+                   && (mydata->from || mydata->to != 0xFFFFFFFF)) {
+                       strncpy(errstr, ip_tostring_numeric(mydata->from),
+                               ERRSTRLEN-2);
+                       errstr[ERRSTRLEN-1] = '\0';
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%s-%s is not a full network (%x)\n",
+                                  errstr,
+                                  ip_tostring_numeric(mydata->to), mask);
+               }
+               netmask_bits = mask_to_bits(mydata->netmask);
+               
+               if (netmask_bits <= mask_bits) {
+                       strncpy(errstr, ip_tostring_numeric(mydata->from),
+                               ERRSTRLEN-2);
+                       errstr[ERRSTRLEN-1] = '\0';
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%d netmask specifies larger or equal netblock than %s-%s (%d)\n",
+                                  netmask_bits,
+                                  errstr,
+                                  ip_tostring_numeric(mydata->to),
+                                  mask_bits);
+               }
+               range = (1<<(netmask_bits - mask_bits)) - 1;
+       } else {
+               range = mydata->to - mydata->from;
+       }
+       if (range > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range to large. Max is %d IPs in range\n",
+                          MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {"network", 1, 0, '3'},
+       {"netmask", 1, 0, '4'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_ipmap *mydata =
+           (struct ip_set_req_ipmap *) data;
+
+       DP("ipmap: %p %p", optarg, data);
+
+       parse_ip(optarg, &mydata->ip);
+       DP("%s", ip_tostring_numeric(mydata->ip));
+
+       return 1;       
+}
+
+/*
+ * Print and save
+ */
+
+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 *) set->settype->header;
+               
+       memset(map, 0, sizeof(struct ip_set_ipmap));
+       map->first_ip = header->from;
+       map->last_ip = header->to;
+       map->netmask = header->netmask;
+
+       if (map->netmask == 0xFFFFFFFF) {
+               map->hosts = 1;
+               map->sizeid = map->last_ip - map->first_ip + 1;
+       } else {
+               unsigned int mask_bits, netmask_bits;
+               ip_set_ip_t mask;
+       
+               mask = range_to_mask(header->from, header->to, &mask_bits);
+               netmask_bits = mask_to_bits(header->netmask);
+
+               DP("bits: %i %i", mask_bits, netmask_bits);
+               map->hosts = 2 << (32 - netmask_bits - 1);
+               map->sizeid = 2 << (netmask_bits - mask_bits - 1);
+       }
+
+       DP("%i %i", map->hosts, map->sizeid );
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (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));
+       if (mysetdata->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask));
+}
+
+void printips_sorted(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) set->settype->header;
+       ip_set_ip_t id;
+
+       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 saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) 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->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" --netmask %d\n",
+                      mask_to_bits(mysetdata->netmask));
+}
+
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (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, data))
+                       printf("-A %s %s\n",
+                              set->name,
+                              ip_tostring(mysetdata->first_ip 
+                                          + id * mysetdata->hosts,
+                                          options));
+}
+
+void usage(void)
+{
+       printf
+           ("-N set ipmap --from IP --to IP [--netmask CIDR-netmask]\n"
+            "-N set ipmap --network IP/mask [--netmask CIDR-netmask]\n"
+            "-A set IP\n"
+            "-D set IP\n"
+            "-T set IP\n");
+}
+
+static struct settype settype_ipmap = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_ipmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_ipmap),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_ipmap),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips_sorted,   /* We only have sorted version */
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse   = &parse_ip,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_ipmap);
+
+}
diff --git a/ipset/ipset_ipporthash.c b/ipset/ipset_ipporthash.c
new file mode 100644 (file)
index 0000000..1ebbc50
--- /dev/null
@@ -0,0 +1,373 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+#include <asm/types.h>
+
+#include <linux/netfilter_ipv4/ip_set_ipporthash.h>
+#include <linux/netfilter_ipv4/ip_set_jhash.h>
+
+#include "ipset.h"
+
+#define OPT_CREATE_HASHSIZE    0x01U
+#define OPT_CREATE_PROBES      0x02U
+#define OPT_CREATE_RESIZE      0x04U
+#define OPT_CREATE_NETWORK     0x08U
+#define OPT_CREATE_FROM                0x10U
+#define OPT_CREATE_TO          0x20U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_ipporthash_create *mydata =
+           (struct ip_set_req_ipporthash_create *) data;
+
+       DP("create INIT");
+
+       /* Default create parameters */ 
+       mydata->hashsize = 1024;
+       mydata->probes = 8;
+       mydata->resize = 50;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_ipporthash_create *mydata =
+           (struct ip_set_req_ipporthash_create *) data;
+       ip_set_ip_t value;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+
+               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;
+
+       case '2':
+
+               if (string_to_number(optarg, 1, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid probes `%s' specified", optarg);
+
+               mydata->probes = value;
+               *flags |= OPT_CREATE_PROBES;
+
+               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':
+               parse_ip(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  ip_tostring_numeric(mydata->from));
+
+               break;
+
+       case '5':
+               parse_ip(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '6':
+               parse_ipandmask(optarg, &mydata->from, &mydata->to);
+
+               /* Make to the last of from + mask */
+               if (mydata->to)
+                       mydata->to = mydata->from | ~(mydata->to);
+               else {
+                       mydata->from = 0x00000000;
+                       mydata->to = 0xFFFFFFFF;
+               }
+               *flags |= OPT_CREATE_NETWORK;
+
+               DP("--network from %x (%s)", 
+                  mydata->from, ip_tostring_numeric(mydata->from));
+               DP("--network to %x (%s)", 
+                  mydata->to, ip_tostring_numeric(mydata->to));
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_ipporthash_create *mydata =
+           (struct ip_set_req_ipporthash_create *) data;
+
+#ifdef IPSET_DEBUG
+       DP("hashsize %u probes %u resize %u",
+          mydata->hashsize, mydata->probes, mydata->resize);
+#endif
+
+       if (flags & OPT_CREATE_NETWORK) {
+               /* --network */
+               if ((flags & OPT_CREATE_FROM) || (flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --from or --to with --network\n");
+       } else if (flags & (OPT_CREATE_FROM | OPT_CREATE_TO)) {
+               /* --from --to */
+               if (!(flags & OPT_CREATE_FROM) || !(flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       } else {
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to, or --network\n");
+
+       }
+
+       DP("from : %x to: %x diff: %x", 
+          mydata->from, mydata->to,
+          mydata->to - mydata->from);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be higher than to.\n");
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range to large. Max is %d IPs in range\n",
+                          MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"hashsize", 1, 0, '1'},
+       {"probes", 1, 0, '2'},
+       {"resize", 1, 0, '3'},
+       {"from", 1, 0, '4'},
+       {"to", 1, 0, '5'},
+       {"network", 1, 0, '6'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_ipporthash *mydata =
+           (struct ip_set_req_ipporthash *) data;
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       DP("ipporthash: %p %p", optarg, data);
+
+       ptr = strsep(&tmp, "%");
+       parse_ip(ptr, &mydata->ip);
+
+       if (tmp)
+               parse_port(tmp, &mydata->port);
+       else
+               exit_error(PARAMETER_PROBLEM,
+                          "IP address and port must be specified: ip%%port");
+       free(saved);
+       return 1;       
+};
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_ipporthash_create *header =
+           (struct ip_set_req_ipporthash_create *) data;
+       struct ip_set_ipporthash *map =
+               (struct ip_set_ipporthash *) set->settype->header;
+
+       memset(map, 0, sizeof(struct ip_set_ipporthash));
+       map->hashsize = header->hashsize;
+       map->probes = header->probes;
+       map->resize = header->resize;
+       map->first_ip = header->from;
+       map->last_ip = header->to;
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+
+       printf(" from: %s", ip_tostring(mysetdata->first_ip, options));
+       printf(" to: %s", ip_tostring(mysetdata->last_ip, options));
+       printf(" hashsize: %u", mysetdata->hashsize);
+       printf(" probes: %u", mysetdata->probes);
+       printf(" resize: %u\n", mysetdata->resize);
+}
+
+void printips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+       size_t offset = 0;
+       ip_set_ip_t *ipptr, ip;
+       uint16_t port;
+
+       while (offset < len) {
+               ipptr = data + offset;
+               if (*ipptr) {
+                       ip = (*ipptr>>16) + mysetdata->first_ip;
+                       port = (uint16_t) *ipptr;
+                       printf("%s%%%s\n", 
+                              ip_tostring(ip, options),
+                              port_tostring(port, options));
+               }
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) 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));
+       printf(" --hashsize %u --probes %u --resize %u\n",
+              mysetdata->hashsize, mysetdata->probes, mysetdata->resize);
+}
+
+/* Print save for an IP */
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+       size_t offset = 0;
+       ip_set_ip_t *ipptr, ip;
+       uint16_t port;
+
+       while (offset < len) {
+               ipptr = data + offset;
+               if (*ipptr) {
+                       ip = (*ipptr>>16) + mysetdata->first_ip;
+                       port = (uint16_t) *ipptr;
+                       printf("-A %s %s%%%s\n", set->name, 
+                              ip_tostring(ip, options),
+                              port_tostring(port, options));
+               }
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+static char buffer[22];
+
+static char * unpack_ipport_tostring(struct set *set, ip_set_ip_t bip, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+       ip_set_ip_t ip, port;
+       
+       ip = (bip>>16) + mysetdata->first_ip;
+       port = (uint16_t) bip;
+       sprintf(buffer, "%s%%%s", 
+               ip_tostring(ip, options), port_tostring(port, options));
+               
+       return buffer;
+}
+
+void usage(void)
+{
+       printf
+           ("-N set ipporthash --from IP --to IP\n"
+            "   [--hashsize hashsize] [--probes probes ] [--resize resize]\n"
+            "-N set ipporthash --network IP/mask\n"
+            "   [--hashsize hashsize] [--probes probes ] [--resize resize]\n"
+            "-A set IP%%port\n"
+            "-D set IP%%port\n"
+            "-T set IP%%port\n");
+}
+
+static struct settype settype_ipporthash = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_ipporthash_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_ipporthash),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_ipporthash),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips,          /* We only have the unsorted version */
+       .printips_sorted = &printips,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &unpack_ipport_tostring,
+       .bindip_parse = &parse_ip,
+       
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_ipporthash);
+
+}
diff --git a/ipset/ipset_iptree.c b/ipset/ipset_iptree.c
new file mode 100644 (file)
index 0000000..cce9884
--- /dev/null
@@ -0,0 +1,223 @@
+/* Copyright 2005 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/netfilter_ipv4/ip_set_iptree.h>
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_TIMEOUT    0x01U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_iptree_create *mydata =
+           (struct ip_set_req_iptree_create *) data;
+
+       DP("create INIT");
+       mydata->timeout = 0;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_iptree_create *mydata =
+           (struct ip_set_req_iptree_create *) data;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               string_to_number(optarg, 0, UINT_MAX, &mydata->timeout);
+
+               *flags |= OPT_CREATE_TIMEOUT;
+
+               DP("--timeout %u", mydata->timeout);
+
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"timeout", 1, 0, '1'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_iptree *mydata =
+           (struct ip_set_req_iptree *) data;
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       DP("iptree: %p %p", optarg, data);
+
+       ptr = strsep(&tmp, "%");
+       parse_ip(ptr, &mydata->ip);
+
+       if (tmp)
+               string_to_number(tmp, 0, UINT_MAX, &mydata->timeout);
+       else
+               mydata->timeout = 0;    
+
+       free(saved);
+       return 1;       
+}
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_iptree_create *header =
+           (struct ip_set_req_iptree_create *) data;
+       struct ip_set_iptree *map =
+               (struct ip_set_iptree *) set->settype->header;
+               
+       map->timeout = header->timeout;
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_iptree *mysetdata =
+           (struct ip_set_iptree *) set->settype->header;
+
+       if (mysetdata->timeout)
+               printf(" timeout: %u", mysetdata->timeout);
+       printf("\n");
+}
+
+void printips_sorted(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_iptree *mysetdata =
+           (struct ip_set_iptree *) set->settype->header;
+       struct ip_set_req_iptree *req;
+       size_t offset = 0;
+
+       while (len >= offset + sizeof(struct ip_set_req_iptree)) {
+               req = (struct ip_set_req_iptree *)(data + offset);
+               if (mysetdata->timeout)
+                       printf("%s%%%u\n", ip_tostring(req->ip, options),
+                                          req->timeout);
+               else
+                       printf("%s\n", ip_tostring(req->ip, options));
+               offset += sizeof(struct ip_set_req_iptree);
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_iptree *mysetdata =
+           (struct ip_set_iptree *) set->settype->header;
+
+       if (mysetdata->timeout)
+               printf("-N %s %s --timeout %u\n",
+                      set->name, set->settype->typename,
+                      mysetdata->timeout);
+       else
+               printf("-N %s %s\n",
+                      set->name, set->settype->typename);
+}
+
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_iptree *mysetdata =
+           (struct ip_set_iptree *) set->settype->header;
+       struct ip_set_req_iptree *req;
+       size_t offset = 0;
+
+       DP("%s", set->name);
+
+       while (len >= offset + sizeof(struct ip_set_req_iptree)) {
+               req = (struct ip_set_req_iptree *)(data + offset);
+               if (mysetdata->timeout)
+                       printf("-A %s %s%%%u\n",
+                               set->name, 
+                               ip_tostring(req->ip, options),
+                               req->timeout);
+               else
+                       printf("-A %s %s\n", 
+                               set->name,
+                               ip_tostring(req->ip, options));
+               offset += sizeof(struct ip_set_req_iptree);
+       }
+}
+
+void usage(void)
+{
+       printf
+           ("-N set iptree [--timeout value]\n"
+            "-A set IP[%%timeout]\n"
+            "-D set IP\n"
+            "-T set IP\n");
+}
+
+static struct settype settype_iptree = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_iptree_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_iptree),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_iptree),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips_sorted,   /* We only have sorted version */
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse   = &parse_ip,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_iptree);
+
+}
diff --git a/ipset/ipset_macipmap.c b/ipset/ipset_macipmap.c
new file mode 100644 (file)
index 0000000..3ef8fb1
--- /dev/null
@@ -0,0 +1,340 @@
+/* Copyright 2000, 2001, 2002 Joakim Axelsson (gozem@linux.nu)
+ *                            Patrick Schaaf (bof@bof.de)
+ *                            Martin Josefsson (gandalf@wlug.westbo.se)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+#include <linux/if_ether.h>
+
+#include <linux/netfilter_ipv4/ip_set_macipmap.h>
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+#define OPT_CREATE_NETWORK 0x04U
+#define OPT_CREATE_MATCHUNSET  0x08U
+
+#define OPT_ADDDEL_IP      0x01U
+#define OPT_ADDDEL_MAC     0x02U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       DP("create INIT");
+       /* Nothing */
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_macipmap_create *mydata =
+           (struct ip_set_req_macipmap_create *) data;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_ip(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  ip_tostring_numeric(mydata->from));
+
+               break;
+
+       case '2':
+               parse_ip(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '3':
+               parse_ipandmask(optarg, &mydata->from, &mydata->to);
+
+               /* Make to the last of from + mask */
+               mydata->to = mydata->from | (~mydata->to);
+
+               *flags |= OPT_CREATE_NETWORK;
+
+               DP("--network from %x (%s)", 
+                  mydata->from, ip_tostring_numeric(mydata->from));
+               DP("--network to %x (%s)", 
+                  mydata->to, ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '4':
+               mydata->flags |= IPSET_MACIP_MATCHUNSET;
+
+               *flags |= OPT_CREATE_MATCHUNSET;
+
+               DP("--matchunset");
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_macipmap_create *mydata =
+           (struct ip_set_req_macipmap_create *) data;
+
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to, or --network\n");
+
+       if (flags & OPT_CREATE_NETWORK) {
+               /* --network */
+               if ((flags & OPT_CREATE_FROM) || (flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --from or --to with --network\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+
+       DP("from : %x to: %x  diff: %d  match unset: %d", mydata->from,
+          mydata->to, mydata->to - mydata->from,
+          flags & OPT_CREATE_MATCHUNSET);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n");
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range too large. Max is %d IPs in range\n",
+                          MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {"network", 1, 0, '3'},
+       {"matchunset", 0, 0, '4'},
+       {0}
+};
+
+static void parse_mac(const char *mac, unsigned char *ethernet)
+{
+       unsigned int i = 0;
+
+       if (strlen(mac) != ETH_ALEN * 3 - 1)
+               exit_error(PARAMETER_PROBLEM, "Bad mac address `%s'", mac);
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               long number;
+               char *end;
+
+               number = strtol(mac + i * 3, &end, 16);
+
+               if (end == mac + i * 3 + 2 && number >= 0 && number <= 255)
+                       ethernet[i] = number;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad mac address `%s'", mac);
+       }
+}
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_macipmap *mydata =
+           (struct ip_set_req_macipmap *) data;
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       DP("macipmap: %p %p", optarg, data);
+
+       ptr = strsep(&tmp, "%");
+       parse_ip(ptr, &mydata->ip);
+
+       if (tmp)
+               parse_mac(tmp, mydata->ethernet);
+       else
+               memset(mydata->ethernet, 0, ETH_ALEN);  
+
+       free(saved);
+       return 1;       
+}
+
+/*
+ * Print and save
+ */
+
+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;
+
+       memset(map, 0, sizeof(struct ip_set_macipmap));
+       map->first_ip = header->from;
+       map->last_ip = header->to;
+       map->flags = header->flags;
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_macipmap *mysetdata =
+           (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));
+
+       if (mysetdata->flags & IPSET_MACIP_MATCHUNSET)
+               printf(" matchunset");
+       printf("\n");
+}
+
+static void print_mac(unsigned char macaddress[ETH_ALEN])
+{
+       unsigned int i;
+
+       printf("%02X", macaddress[0]);
+       for (i = 1; i < ETH_ALEN; i++)
+               printf(":%02X", macaddress[i]);
+}
+
+void printips_sorted(struct set *set, void *data, size_t len, unsigned options)
+{
+       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("%s%%", ip_tostring(addr, options));
+                       print_mac(table[addr - mysetdata->first_ip].
+                                 ethernet);
+                       printf("\n");
+               }
+               addr++;
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       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");
+}
+
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       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)
+{
+       printf
+           ("-N set macipmap --from IP --to IP [--matchunset]\n"
+            "-N set macipmap --network IP/mask [--matchunset]\n"
+            "-A set IP%%MAC\n"
+            "-D set IP[%%MAC]\n"
+            "-T set IP[%%MAC]\n");
+}
+
+static struct settype settype_macipmap = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_macipmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_macipmap),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_macipmap),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips_sorted,   /* We only have sorted version */
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+
+       /* Bindings */
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse = &parse_ip,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_macipmap);
+
+}
diff --git a/ipset/ipset_nethash.c b/ipset/ipset_nethash.c
new file mode 100644 (file)
index 0000000..758c4c1
--- /dev/null
@@ -0,0 +1,366 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+#include <asm/types.h>
+
+#include <linux/netfilter_ipv4/ip_set_nethash.h>
+#include <linux/netfilter_ipv4/ip_set_jhash.h>
+
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_HASHSIZE    0x01U
+#define OPT_CREATE_PROBES      0x02U
+#define OPT_CREATE_RESIZE      0x04U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_nethash_create *mydata =
+           (struct ip_set_req_nethash_create *) data;
+
+       DP("create INIT");
+
+       /* Default create parameters */ 
+       mydata->hashsize = 1024;
+       mydata->probes = 4;
+       mydata->resize = 50;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_nethash_create *mydata =
+           (struct ip_set_req_nethash_create *) data;
+       ip_set_ip_t value;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+
+               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;
+
+       case '2':
+
+               if (string_to_number(optarg, 1, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid probes `%s' specified", optarg);
+
+               mydata->probes = value;
+               *flags |= OPT_CREATE_PROBES;
+
+               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;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+#ifdef IPSET_DEBUG
+       struct ip_set_req_nethash_create *mydata =
+           (struct ip_set_req_nethash_create *) data;
+
+       DP("hashsize %u probes %u resize %u",
+          mydata->hashsize, mydata->probes, mydata->resize);
+#endif
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"hashsize", 1, 0, '1'},
+       {"probes", 1, 0, '2'},
+       {"resize", 1, 0, '3'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_nethash *mydata =
+           (struct ip_set_req_nethash *) data;
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+       ip_set_ip_t cidr;
+
+       ptr = strsep(&tmp, "/");
+       
+       if (tmp == NULL) {
+               if (cmd == CMD_TEST)
+                       cidr = 32;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Missing cidr from `%s'", optarg);
+       } else
+               if (string_to_number(tmp, 1, 31, &cidr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Out of range cidr `%s' specified", optarg);
+       
+       mydata->cidr = cidr;
+       parse_ip(ptr, &mydata->ip);
+       if (!mydata->ip)
+               exit_error(PARAMETER_PROBLEM,
+                         "Zero valued IP address `%s' specified", ptr);
+       free(saved);
+
+       return mydata->ip;      
+};
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_nethash_create *header =
+           (struct ip_set_req_nethash_create *) data;
+       struct ip_set_nethash *map =
+               (struct ip_set_nethash *) set->settype->header;
+
+       memset(map, 0, sizeof(struct ip_set_nethash));
+       map->hashsize = header->hashsize;
+       map->probes = header->probes;
+       map->resize = header->resize;
+}
+
+unsigned int
+mask_to_bits(ip_set_ip_t mask)
+{
+       unsigned int bits = 32;
+       ip_set_ip_t maskaddr;
+       
+       if (mask == 0xFFFFFFFF)
+               return bits;
+       
+       maskaddr = 0xFFFFFFFE;
+       while (--bits >= 0 && maskaddr != mask)
+               maskaddr <<= 1;
+       
+       return bits;
+}
+       
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_nethash *mysetdata =
+           (struct ip_set_nethash *) set->settype->header;
+
+       printf(" hashsize: %u", mysetdata->hashsize);
+       printf(" probes: %u", mysetdata->probes);
+       printf(" resize: %u\n", mysetdata->resize);
+}
+
+static char buf[20];
+
+static char * unpack_ip_tostring(ip_set_ip_t ip, unsigned options)
+{
+       int i, j = 3;
+       unsigned char a, b;
+
+       ip = htonl(ip); 
+       for (i = 3; i >= 0; i--)
+               if (((unsigned char *)&ip)[i] != 0) {
+                       j = i;
+                       break;
+               }
+                       
+       a = ((unsigned char *)&ip)[j];
+       if (a <= 128) {
+               a = (a - 1) * 2;
+               b = 7;
+       } else if (a <= 192) {
+               a = (a - 129) * 4;
+               b = 6;
+       } else if (a <= 224) {
+               a = (a - 193) * 8;
+               b = 5;
+       } else if (a <= 240) {
+               a = (a - 225) * 16;
+               b = 4;
+       } else if (a <= 248) {
+               a = (a - 241) * 32;
+               b = 3;
+       } else if (a <= 252) {
+               a = (a - 249) * 64;
+               b = 2;
+       } else if (a <= 254) {
+               a = (a - 253) * 128;
+               b = 1;
+       } else {
+               a = b = 0;
+       }
+       ((unsigned char *)&ip)[j] = a;
+       b += j * 8;
+       
+       sprintf(buf, "%u.%u.%u.%u/%u",
+               ((unsigned char *)&ip)[0],
+               ((unsigned char *)&ip)[1],
+               ((unsigned char *)&ip)[2],
+               ((unsigned char *)&ip)[3],
+               b);
+
+       DP("%s %s", ip_tostring(ntohl(ip), options), buf);
+       return buf;
+}
+
+void printips(struct set *set, void *data, size_t len, unsigned options)
+{
+       size_t offset = 0;
+       ip_set_ip_t *ip;
+
+       while (offset < len) {
+               ip = data + offset;
+               if (*ip)
+                       printf("%s\n", unpack_ip_tostring(*ip, options));
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_nethash *mysetdata =
+           (struct ip_set_nethash *) set->settype->header;
+
+       printf("-N %s %s --hashsize %u --probes %u --resize %u\n",
+              set->name, set->settype->typename,
+              mysetdata->hashsize, mysetdata->probes, mysetdata->resize);
+}
+
+/* Print save for an IP */
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       size_t offset = 0;
+       ip_set_ip_t *ip;
+
+       while (offset < len) {
+               ip = data + offset;
+               if (*ip)
+                       printf("-A %s %s\n", set->name, 
+                              unpack_ip_tostring(*ip, options));
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+static char * net_tostring(struct set *set, ip_set_ip_t ip, unsigned options)
+{
+       return unpack_ip_tostring(ip, options);
+}
+
+static void parse_net(const char *str, ip_set_ip_t *ip)
+{
+       char *saved = strdup(str);
+       char *ptr, *tmp = saved;
+       ip_set_ip_t cidr;
+
+       ptr = strsep(&tmp, "/");
+       
+       if (tmp == NULL)
+               exit_error(PARAMETER_PROBLEM,
+                          "Missing cidr from `%s'", str);
+
+       if (string_to_number(tmp, 1, 31, &cidr))
+               exit_error(PARAMETER_PROBLEM,
+                          "Out of range cidr `%s' specified", str);
+       
+       parse_ip(ptr, ip);
+       free(saved);
+       
+       *ip = pack(*ip, cidr);
+}
+
+void usage(void)
+{
+       printf
+           ("-N set nethash [--hashsize hashsize] [--probes probes ]\n"
+            "               [--resize resize]\n"
+            "-A set IP/cidr\n"
+            "-D set IP/cidr\n"
+            "-T set IP/cidr\n");
+}
+
+static struct settype settype_nethash = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_nethash_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_nethash),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_nethash),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips,          /* We only have the unsorted version */
+       .printips_sorted = &printips,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &net_tostring,
+       .bindip_parse = &parse_net,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_nethash);
+
+}
diff --git a/ipset/ipset_portmap.c b/ipset/ipset_portmap.c
new file mode 100644 (file)
index 0000000..1c3965b
--- /dev/null
@@ -0,0 +1,245 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/bitops.h>
+
+#include <linux/netfilter_ipv4/ip_set_portmap.h>
+#include "ipset.h"
+
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+
+#define OPT_ADDDEL_PORT      0x01U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       DP("create INIT");
+       /* Nothing */
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_portmap_create *mydata =
+           (struct ip_set_req_portmap_create *) data;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_port(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  port_tostring(mydata->from, 0));
+
+               break;
+
+       case '2':
+               parse_port(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  port_tostring(mydata->to, 0));
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_portmap_create *mydata =
+           (struct ip_set_req_portmap_create *) data;
+
+       if (flags == 0) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+       DP("from : %x to: %x  diff: %d", mydata->from, mydata->to,
+          mydata->to - mydata->from);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n");
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range too large. Max is %d ports in range\n",
+                          MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_portmap *mydata =
+           (struct ip_set_req_portmap *) data;
+
+       parse_port(optarg, &mydata->port);
+       DP("%s", port_tostring(mydata->port, 0));
+
+       return 1;       
+}
+
+/*
+ * Print and save
+ */
+
+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;
+
+       memset(map, 0, sizeof(struct ip_set_portmap));
+       map->first_port = header->from;
+       map->last_port = header->to;
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_portmap *mysetdata =
+           (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(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_portmap *mysetdata =
+           (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, data))
+                       printf("%s\n", port_tostring(addr, options));
+               addr++;
+       }
+}
+
+char * binding_port_tostring(struct set *set, ip_set_ip_t ip, unsigned options)
+{
+       return port_tostring(ip, options);
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       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));
+}
+
+void saveports(struct set *set, void *data, size_t len, unsigned options)
+{
+       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)
+{
+       printf
+           ("-N set portmap --from PORT --to PORT\n"
+            "-A set PORT\n"
+            "-D set PORT\n"
+            "-T set PORT\n");
+}
+
+static struct settype settype_portmap = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_portmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_portmap),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_portmap),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printports_sorted, /* We only have sorted version */
+       .printips_sorted = &printports_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveports,
+       
+       /* Bindings */
+       .bindip_tostring = &binding_port_tostring,
+       .bindip_parse = &parse_port,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_portmap);
+
+}
diff --git a/ipset/libipt_set.h b/ipset/libipt_set.h
new file mode 100644 (file)
index 0000000..0521251
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef _LIBIPT_SET_H
+#define _LIBIPT_SET_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+static int get_set_getsockopt(void *data, size_t * size)
+{
+       int sockfd = -1;
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               exit_error(OTHER_PROBLEM,
+                          "Can't open socket to ipset.\n");
+       /* Send! */
+       return getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+}
+
+static void
+parse_bindings(const char *optarg, struct ipt_set_info *info)
+{
+       char *saved = strdup(optarg);
+       char *ptr, *tmp = saved;
+       int i = 0;
+       
+       while (i < IP_SET_MAX_BINDINGS && tmp != NULL) {
+               ptr = strsep(&tmp, ",");
+               if (strncmp(ptr, "src", 3) == 0)
+                       info->flags[i++] |= IPSET_SRC;
+               else if (strncmp(ptr, "dst", 3) == 0)
+                       info->flags[i++] |= IPSET_DST;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "You must spefify (the comma separated list of) 'src' or 'dst'.");
+       }
+
+       if (tmp)
+               exit_error(PARAMETER_PROBLEM,
+                          "Can't follow bindings deeper than %i.", 
+                          IP_SET_MAX_BINDINGS);
+
+       free(saved);
+}
+
+#endif /*_LIBIPT_SET_H*/