summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ipset.8537
-rw-r--r--src/ipset.c2054
-rw-r--r--src/ipset.h200
-rw-r--r--src/ipset_iphash.c279
-rw-r--r--src/ipset_ipmap.c376
-rw-r--r--src/ipset_ipporthash.c350
-rw-r--r--src/ipset_ipportiphash.c361
-rw-r--r--src/ipset_ipportnethash.c426
-rw-r--r--src/ipset_iptree.c224
-rw-r--r--src/ipset_iptreemap.c208
-rw-r--r--src/ipset_macipmap.c382
-rw-r--r--src/ipset_nethash.c308
-rw-r--r--src/ipset_portmap.c272
-rw-r--r--src/ipset_setlist.c229
14 files changed, 6206 insertions, 0 deletions
diff --git a/src/ipset.8 b/src/ipset.8
new file mode 100644
index 0000000..fa73298
--- /dev/null
+++ b/src/ipset.8
@@ -0,0 +1,537 @@
+.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 \(em administration tool for IP sets
+.SH SYNOPSIS
+.PP
+\fBipset \-N\fP \fIset\fP \fItype-specification\fP [\fIoptions\fP...]
+.PP
+\fBipset\fP {\fB\-F\fP|\fB\-H\fP|\fB\-L\fP|\fB\-S\fP|\fB\-X\fP} [\fIset\fP]
+[\fIoptions\fP...]
+.PP
+\fBipset\fP {\fB\-E\fP|\fB\-W\fP} \fIfrom-set\fP \fIto-set\fP
+.PP
+\fBipset\fP {\fB\-A\fP|\fB\-D\fP|\fB\-T\fP} \fIset\fP \fIentry\fP
+.PP
+\fBipset \-R\fP
+.PP
+\fBipset\fP {\fB-V\fP|\fB\-v\fP}
+.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
+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
+\fB\-N\fP, \fB\-\-create\fP \fIsetname\fP \fItype\fP \fItype-specific-options\fP
+Create a set identified with setname and specified type.
+Type-specific options must be supplied.
+.TP
+\fB\-X\fP, \fB\-\-destroy\fP [\fIsetname\fP]
+Destroy the specified set or all the sets if none is given.
+
+If the set has got references, nothing is done.
+.TP
+\fB\-F\fP, \fB\-\-flush\fP [\fIsetname\fP]
+Delete all entries from the specified set or flush
+all sets if none is given.
+.TP
+\fB\-E\fP, \fB\-\-rename\fP \fIfrom-setname\fP \fIto-setname\fP
+Rename a set. Set identified by to-setname must not exist.
+.TP
+\fB\-W\fP, \fB\-\-swap\fP \fIfrom-setname\fP \fIto-setname\fP
+Swap the content of two sets, or in another words,
+exchange the name of two sets. The referred sets must exist and
+identical type of sets can be swapped only.
+.TP
+\fB\-L\fP, \fB\-\-list\fP [\fIsetname\fP]
+List the entries for the specified set, or for
+all sets if none is given. The
+\fB\-r\fP/\fB\-\-resolve\fP
+option can be used to force name lookups (which may be slow). When the
+\fB\-s\fP/\fB\-\-sorted\fP
+option is given, the entries are listed sorted (if the given set
+type supports the operation).
+.TP
+\fB\-S\fP, \fB\-\-save\fP [\fIsetname\fP]
+Save the given set, or all sets if none is given
+to stdout in a format that \fB\-\-restore\fP can read.
+.TP
+\fB\-R\fP, \fB\-\-restore\fP
+Restore a saved session generated by \fB\-\-save\fP. The saved session
+can be fed from stdin.
+
+When generating a session file please note that the supported commands
+(create set and add element) 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. Also, it is a restore operation, so the sets being restored must
+not exist.
+.TP
+\fB\-A\fP, \fB\-\-add\fP \fIsetname\fP \fIentry\fP
+Add an entry to a set.
+.TP
+\fB\-D\fP, \fB\-\-del\fP \fIsetname\fP \fIentry\fP
+Delete an entry from a set.
+.TP
+\fB-T\fP, \fB\-\-test\fP \fIsetname\fP \fIentry\fP
+Test wether an entry is in a set or not. Exit status number is zero
+if the tested entry is in the set and nonzero if it is missing from
+the set.
+.TP
+\fB\-H\fP, \fB\-\-help\fP [\fIsettype\fP]
+Print help and settype specific help if settype specified.
+.TP
+\fB\-V\fP, \fB\-v\fP, \fB\-\-version\fP
+Print program version and protocol version.
+.P
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+\fB\-r\fP, \fB\-\-resolve\fP
+When listing sets, enforce name lookup. The
+program will try to display the IP entries resolved to
+host names or services (whenever applicable), which can trigger
+.B
+slow
+DNS
+lookups.
+.TP
+\fB\-s\fP, \fB\-\-sorted\fP
+Sorted output. When listing sets, entries are listed sorted.
+.TP
+\fB\-n\fP, \fB\-\-numeric\fP
+Numeric output. When listing sets, IP addresses and
+port numbers will be printed in numeric format. This is the default.
+.TP
+\fB\-q\fP, \fB\-\-quiet\fP
+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. If the optional
+\fB\-\-netmask\fP
+parameter is specified with a CIDR netmask value between 1-31 then
+network addresses are stored in the given set: i.e an
+IP address will be in the set if the network address, which is 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
+\fB\-\-from\fP \fIfrom-addr\fP
+.TP
+\fB\-\-to\fP \fIto-addr\fP
+Create an ipmap set from the specified address range.
+.TP
+\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP
+Create an ipmap set from the specified network.
+.TP
+\fB\-\-netmask\fP \fIprefixlen\fP
+When the optional
+\fB\-\-netmask\fP
+parameter specified, network addresses will be
+stored in the set instead of IP addresses, and the \fIfrom-addr\fP parameter
+must be a network address. The \fIprefixlen\fP value must be between 1-31.
+.PP
+Example:
+.IP
+ipset \-N test ipmap \-\-network 192.168.0.0/16
+.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
+"\fIaddress\fP\fB,\fP\fImac\fP".
+When deleting or testing macipmap entries, the
+"\fB,\fP\fImac\fP"
+part is not mandatory.
+.P
+Options to use when creating an macipmap set:
+.TP
+\fB\-\-from\fP \fIfrom-addr\fP
+.TP
+\fB\-\-to\fP \fIto-addr\fP
+Create a macipmap set from the specified address range.
+.TP
+\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP
+Create a macipmap set from the specified network.
+.TP
+\fB\-\-matchunset\fP
+When the optional
+\fB\-\-matchunset\fP
+parameter specified, IP addresses which could be stored
+in the set but not set yet, will always match.
+.P
+Please note, the
+"set"
+and
+"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
+\fB\-\-from\fP \fIfrom-port\fP
+.TP
+\fB\-\-to\fP \fIto-port\fP
+Create a portmap set from the specified port 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. If the optional
+\fB\-\-netmask\fP
+parameter is specified with a CIDR prefix length value between 1-31 then
+network addresses are stored in the given set: i.e an
+IP address will be in the set if the network address, which is resulted
+by masking the address with the specified netmask, can be found in the set.
+.P
+Options to use when creating an iphash set:
+.TP
+\fB\-\-hashsize\fP \fIhashsize\fP
+The initial hash size (default 1024)
+.TP
+\fB\-\-probes\fP \fIprobes\fP
+How many times try to resolve clashing at adding an IP to the hash
+by double-hashing (default 8).
+.TP
+\fB\-\-resize\fP \fIpercent\fP
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+\fIprobes\fP
+number of double-hashing.
+.TP
+\fB\-\-netmask\fP \fIprefixlen\fP
+When the optional
+\fB\-\-netmask\fP
+parameter specified, network addresses will be
+stored in the set instead of IP addresses. The \fIprefixlen\fP value must
+be between 1-31.
+.P
+The iphash type of sets can store up to 65536 entries. If a set is full,
+no new entries can be added to it.
+.P
+Sets created by zero valued resize parameter won't be resized at all.
+The lookup time in an iphash type of set grows approximately linearly with
+the value of the
+\fIprobes\fP
+parameter. In general higher
+\fIprobes\fP
+value results better utilized hash while smaller value
+produces larger, sparser hash.
+.PP
+Example:
+.IP
+ipset \-N test iphash \-\-probes 2
+.SS nethash
+The nethash set type uses a hash to store different size of
+network addresses. The
+.I
+entry
+used in the ipset commands must be in the form
+"\fIaddress\fP\fB/\fP\fIprefixlen\fP"
+where prefixlen 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
+\fB\-\-hashsize\fP \fIhashsize\fP
+The initial hash size (default 1024)
+.TP
+\fB\-\-probes\fP \fIprobes\fP
+How many times try to resolve clashing at adding an IP to the hash
+by double-hashing (default 4).
+.TP
+\fB\-\-resize\fP \fIpercent\fP
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+.P
+The nethash type of sets can store up to 65536 entries. If a set is full,
+no new entries can be added to it.
+.P
+An IP address will be in a nethash type of set if it belongs to any of the
+netblocks added to the set. The matching always start from the smallest
+size of netblock (most specific netmask) to the largest ones (least
+specific netmasks). When adding/deleting IP addresses
+to a nethash set by the
+"SET"
+netfilter kernel module, it will be added/deleted by the smallest
+netblock size which can be found in the set, or by /31 if the set is empty.
+.P
+The lookup time in a nethash type of set grows approximately linearly
+with the times of the
+\fIprobes\fP
+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
+"\fIaddress\fP\fB,\fP\fIport\fP".
+.P
+The ipporthash types of sets evaluates two src/dst parameters of the
+"set"
+match and
+"SET"
+target.
+.P
+Options to use when creating an ipporthash set:
+.TP
+\fB\-\-from\fP \fIfrom-addr\fP
+.TP
+\fB\-\-to\fP \fIto-addr\fP
+Create an ipporthash set from the specified address range.
+.TP
+\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP
+Create an ipporthash set from the specified network.
+.TP
+\fB\-\-hashsize\fP \fIhashsize\fP
+The initial hash size (default 1024)
+.TP
+\fB\-\-probes\fP \fIprobes\fP
+How many times try to resolve clashing at adding an IP to the hash
+by double-hashing (default 8).
+.TP
+\fB\-\-resize\fP \fIpercent\fP
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+\fIprobes\fP
+number of double-hashing.
+.P
+The same resizing, speed and memory efficiency comments applies here
+as at the iphash type.
+.SS ipportiphash
+The ipportiphash set type uses a hash to store IP address,port and IP
+address triples. The first IP address must come form a maximum /16
+sized network or range while the port number and the second IP address
+parameters are arbitrary. When adding, deleting and testing values in an
+ipportiphash type of set, the entries must be specified as
+"\fIaddress\fP\fB,\fP\fIport\fP\fB,\fP\fIaddress\fP".
+.P
+The ipportiphash types of sets evaluates three src/dst parameters of the
+"set"
+match and
+"SET"
+target.
+.P
+Options to use when creating an ipportiphash set:
+.TP
+\fB\-\-from\fP \fIfrom-addr\fP
+.TP
+\fB\-\-to\fP \fIto-addr\fP
+Create an ipportiphash set from the specified address range.
+.TP
+\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP
+Create an ipportiphash set from the specified network.
+.TP
+\fB\-\-hashsize\fP \fIhashsize\fP
+The initial hash size (default 1024)
+.TP
+\fB\-\-probes\fP \fIprobes\fP
+How many times try to resolve clashing at adding an IP to the hash
+by double-hashing (default 8).
+.TP
+\fB\-\-resize\fP \fIpercent\fP
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+\fIprobes\fP
+number of double-hashing.
+.P
+The same resizing, speed and memory efficiency comments applies here
+as at the iphash type.
+.SS ipportnethash
+The ipportnethash set type uses a hash to store IP address, port, and
+network address triples. The IP address must come form a maximum /16
+sized network or range while the port number and the network address
+parameters are arbitrary, but the size of the network address must be
+between /1-/31. When adding, deleting
+and testing values in an ipportnethash type of set, the entries must be
+specified as
+"\fIaddress\fP\fB,\fP\fIport\fP\fB,\fP\fIaddress\fP\fB/\fP\fIprefixlen\fP".
+.P
+The ipportnethash types of sets evaluates three src/dst parameters of the
+"set"
+match and
+"SET"
+target.
+.P
+Options to use when creating an ipportnethash set:
+.TP
+\fB\-\-from\fP \fIfrom-address\fP
+.TP
+\fB\-\-to\fP \fIto-address\fP
+Create an ipporthash set from the specified range.
+.TP
+\fB\-\-network\fP \fIaddress\fP\fB/\fP\fImask\fP
+Create an ipporthash set from the specified network.
+.TP
+\fB\-\-hashsize\fP \fIhashsize\fP
+The initial hash size (default 1024)
+.TP
+\fB\-\-probes\fP \fIprobes\fP
+How many times try to resolve clashing at adding an IP to the hash
+by double-hashing (default 8).
+.TP
+\fB\-\-resize\fP \fIpercent\fP
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+\fIprobes\fP
+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
+\fB\-\-timeout\fP \fIvalue\fP
+The timeout value for the entries in seconds (default 0)
+.P
+If a set was created with a nonzero valued
+\fB\-\-timeout\fP
+parameter then one may add IP addresses to the set with a specific
+timeout value using the syntax
+"\fIaddress\fP\fB,\fP\fItimeout-value\fP".
+Similarly to the hash types, the iptree type of sets can store up to 65536
+entries.
+.SS iptreemap
+The iptreemap set type uses a tree to store IP addresses or networks,
+where the last octet of an IP address are stored in a bitmap.
+As input entry, you can add IP addresses, CIDR blocks or network ranges
+to the set. Network ranges can be specified in the format
+"\fIaddress1\fP\fB-\fP\fIaddress2\fP".
+.P
+Options to use when creating an iptreemap set:
+.TP
+\fB\-\-gc\fP \fIvalue\fP
+How often the garbage collection should be called, in seconds (default 300)
+.SS setlist
+The setlist type uses a simple list in which you can store sets. By the
+ipset
+command you can add, delete and test sets in a setlist type of set.
+You can specify the sets as
+"\fIsetname\fP[\fB,\fP{\fBafter\fP|\fBbefore\fP},\fIsetname\fP]".
+By default new sets are added after (appended to) the existing
+elements. Setlist type of sets cannot be added to a setlist type of set.
+.P
+Options to use when creating a setlist type of set:
+.TP
+\fB\-\-size\fP \fIsize\fP
+Create a setlist type of set with the given size (default 8).
+.PP
+By the
+"set"
+match or
+"SET"
+target of
+\fBiptables\fP(8)
+you can test, add or delete entries in the sets. The match
+will try to find a matching IP address/port in the sets and
+the target will try to add the IP address/port to the first set
+to which it can be added. The number of src,dst options of
+the match and target are important: sets which eats more src,dst
+parameters than specified are skipped, while sets with equal
+or less parameters are checked, elements added. For example
+if
+.I
+a
+and
+.I
+b
+are setlist type of sets then in the command
+.IP
+iptables \-m set \-\-match\-set a src,dst \-j SET \-\-add-set b src,dst
+.PP
+the match and target will skip any set in
+.I a
+and
+.I b
+which stores
+data triples, but will check all sets with single or double
+data storage in
+.I a
+set and add src to the first single or src,dst to the first double
+data storage set in
+\fIb\fP.
+.P
+You can imagine a setlist type of set as an ordered union of
+the set elements.
+.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.
+.P
+Old separator tokens (':' and '%") are still accepted.
+.P
+Binding support is removed.
+.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.
+.P
+Sven Wegener wrote the iptreemap type.
+.SH LAST REMARK
+.BR "I stand on the shoulders of giants."
diff --git a/src/ipset.c b/src/ipset.c
new file mode 100644
index 0000000..3b8e248
--- /dev/null
+++ b/src/ipset.c
@@ -0,0 +1,2054 @@
+/* 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> /* *printf, perror, sscanf, fdopen */
+#include <string.h> /* mem*, str* */
+#include <errno.h> /* errno, perror */
+#include <time.h> /* time, ctime */
+#include <netdb.h> /* gethostby*, getnetby*, getservby* */
+#include <stdlib.h> /* exit, malloc, free, strtol, getenv, mkstemp */
+#include <unistd.h> /* read, close, fork, exec*, unlink */
+#include <sys/types.h> /* open, wait, socket, *sockopt, umask */
+#include <sys/stat.h> /* open, umask */
+#include <sys/wait.h> /* wait */
+#include <sys/socket.h> /* socket, *sockopt, gethostby*, inet_* */
+#include <netinet/in.h> /* inet_* */
+#include <fcntl.h> /* open */
+#include <arpa/inet.h> /* htonl, inet_* */
+#include <stdarg.h> /* va_* */
+#include <dlfcn.h> /* dlopen */
+
+#include "ipset.h"
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+char program_name[] = "ipset";
+char program_version[] = IPSET_VERSION;
+static int protocol_version = 0;
+
+#define STREQ(a,b) (strncmp(a,b,IP_SET_MAXNAMELEN) == 0)
+#define DONT_ALIGN (protocol_version == IP_SET_PROTOCOL_UNALIGNED)
+#define ALIGNED(len) IPSET_VALIGN(len, DONT_ALIGN)
+
+/* 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;
+socklen_t restore_size;
+unsigned restore_line = 0;
+unsigned warn_once = 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', '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_RESOLVE 0x0020U /* -r */
+#define NUMBER_OF_OPT 5
+static const char optflags[] =
+ { 'n', 's', 'q', 'z', 'r' };
+
+static struct option opts_long[] = {
+ /* set operations */
+ {"create", 1, 0, 'N'},
+ {"destroy", 2, 0, 'X'},
+ {"flush", 2, 0, 'F'},
+ {"rename", 1, 0, 'E'},
+ {"swap", 1, 0, 'W'},
+ {"list", 2, 0, 'L'},
+
+ {"save", 2, 0, 'S'},
+ {"restore", 0, 0, 'R'},
+
+ /* ip in set operations */
+ {"add", 1, 0, 'A'},
+ {"del", 1, 0, 'D'},
+ {"test", 1, 0, 'T'},
+
+ /* free options */
+ {"numeric", 0, 0, 'n'},
+ {"sorted", 0, 0, 's'},
+ {"quiet", 0, 0, 'q'},
+ {"resolve", 0, 0, 'r'},
+
+#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 */
+ {NULL},
+};
+
+static char opts_short[] =
+ "-N:X::F::E:W:L::S::RA:D:T:nrsqzvVh::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 -r */
+ /*CREATE*/ {'x', 'x', ' ', ' ', 'x'},
+ /*DESTROY*/ {'x', 'x', ' ', ' ', 'x'},
+ /*FLUSH*/ {'x', 'x', ' ', ' ', 'x'},
+ /*RENAME*/ {'x', 'x', ' ', ' ', 'x'},
+ /*SWAP*/ {'x', 'x', ' ', ' ', 'x'},
+ /*LIST*/ {' ', ' ', 'x', ' ', ' '},
+ /*SAVE*/ {'x', 'x', ' ', ' ', 'x'},
+ /*RESTORE*/ {'x', 'x', ' ', ' ', 'x'},
+ /*ADD*/ {'x', 'x', ' ', ' ', 'x'},
+ /*DEL*/ {'x', 'x', ' ', ' ', 'x'},
+ /*TEST*/ {'x', 'x', ' ', ' ', 'x'},
+ /*HELP*/ {'x', 'x', 'x', ' ', 'x'},
+ /*VERSION*/ {'x', 'x', 'x', ' ', 'x'},
+};
+
+/* Main parser function */
+int parse_commandline(int argc, char *argv[]);
+
+static 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(int status, const char *msg, ...)
+{
+ if (!option_quiet) {
+ va_list args;
+
+ va_start(args, msg);
+ fprintf(stderr, "%s v%s: ", program_name, program_version);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ if (restore_line)
+ fprintf(stderr, "Restore failed at line %u:\n", restore_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);
+}
+
+static void ipset_printf(const char *msg, ...)
+{
+ if (!option_quiet) {
+ va_list args;
+
+ va_start(args, msg);
+ vfprintf(stdout, msg, args);
+ va_end(args);
+ fprintf(stdout, "\n");
+ }
+}
+
+static void generic_opt_check(int command, unsigned 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(unsigned int option)
+{
+ const char *ptr;
+ for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+ return *ptr;
+}
+
+static char cmd2char(int cmd)
+{
+ if (cmd <= CMD_NONE || cmd > NUMBER_OF_CMD)
+ return ' ';
+
+ return cmdflags[cmd];
+}
+
+/* From iptables.c ... */
+static char *get_modprobe(void)
+{
+ int procfile;
+ char *ret;
+
+#define PROCFILE_BUFSIZ 1024
+ procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+ if (procfile < 0)
+ return NULL;
+
+ ret = (char *) malloc(PROCFILE_BUFSIZ);
+ if (ret) {
+ memset(ret, 0, PROCFILE_BUFSIZ);
+ switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
+ case -1: goto fail;
+ case PROCFILE_BUFSIZ: goto fail; /* Partial read. Wierd */
+ default: ; /* nothing */
+ }
+ if (ret[strlen(ret)-1]=='\n')
+ ret[strlen(ret)-1]=0;
+ close(procfile);
+ return ret;
+ }
+ fail:
+ free(ret);
+ close(procfile);
+ return NULL;
+}
+
+static int ipset_insmod(const char *modname, const char *modprobe)
+{
+ char *buf = NULL;
+ char *argv[3];
+ struct stat junk;
+ int status;
+
+ if (!stat(modprobe, &junk)) {
+ /* Try to read out of the kernel */
+ buf = get_modprobe();
+ if (!buf)
+ return -1;
+ modprobe = buf;
+ }
+
+ switch (fork()) {
+ case 0:
+ argv[0] = (char *) modprobe;
+ argv[1] = (char *) modname;
+ argv[2] = NULL;
+ execv(argv[0], argv);
+
+ /* Should not reach */
+ exit(1);
+ case -1:
+ return -1;
+
+ default: /* parent */
+ wait(&status);
+ }
+
+ free(buf);
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ return 0;
+ return -1;
+}
+
+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;
+ const 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/element is outside of the set or set is full" },
+ { 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" },
+ { EEXIST, 0, "Set specified as element does not exist" },
+ { 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 inline int wrapped_getsockopt(void *data, socklen_t *size)
+{
+ int res;
+ int sockfd = kernel_getsocket();
+
+ /* Send! */
+ res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+ if (res != 0
+ && errno == ENOPROTOOPT
+ && ipset_insmod("ip_set", "/sbin/modprobe") == 0)
+ res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+ DP("res=%d errno=%d", res, errno);
+
+ return res;
+}
+
+static inline int wrapped_setsockopt(void *data, socklen_t size)
+{
+ int res;
+ int sockfd = kernel_getsocket();
+
+ /* Send! */
+ res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+ if (res != 0
+ && errno == ENOPROTOOPT
+ && ipset_insmod("ip_set", "/sbin/modprobe") == 0)
+ res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+ DP("res=%d errno=%d", res, errno);
+
+ return res;
+}
+
+static void kernel_getfrom(unsigned cmd, void *data, socklen_t * size)
+{
+ int res = wrapped_getsockopt(data, size);
+
+ if (res != 0)
+ kernel_error(cmd, errno);
+}
+
+static int kernel_sendto_handleerrno(unsigned cmd,
+ void *data, socklen_t size)
+{
+ int res = wrapped_setsockopt(data, size);
+
+ 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 = wrapped_setsockopt(data, size);
+
+ if (res != 0)
+ kernel_error(cmd, errno);
+}
+
+static int kernel_getfrom_handleerrno(unsigned cmd, void *data, socklen_t *size)
+{
+ int res = wrapped_getsockopt(data, size);
+
+ 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;
+ socklen_t size = sizeof(struct ip_set_req_version);
+ int res;
+
+ if (protocol_version)
+ return;
+
+ req_version.op = IP_SET_OP_VERSION;
+ res = wrapped_getsockopt(&req_version, &size);
+
+ if (res != 0)
+ exit_error(OTHER_PROBLEM,
+ "Couldn't verify kernel module version!");
+
+ if (!(req_version.version == IP_SET_PROTOCOL_VERSION
+ || req_version.version == IP_SET_PROTOCOL_UNALIGNED))
+ exit_error(OTHER_PROBLEM,
+ "Kernel ip_set module 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);
+ protocol_version = req_version.version;
+}
+
+static void set_command(int *cmd, 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);
+}
+
+static struct option *merge_options(struct option *oldopts,
+ const struct option *newopts,
+ 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(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 *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);
+
+ memcpy(&addr, host->h_addr_list[0], sizeof(struct in_addr));
+ *ip = ntohl(addr.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;
+ 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)
+{
+ unsigned long number;
+ char *end;
+
+ /* Handle hex, octal, etc. */
+ errno = 0;
+ number = strtoul(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((uint16_t) 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 (STREQ(runner->typename, typename))
+ 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 */
+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;
+}
+
+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] != NULL && STREQ(set_list[i]->name, name)) {
+ 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, idx = IP_SET_INVALID_ID;
+
+ for (i = 0; i < max_sets; i++) {
+ if (idx == IP_SET_INVALID_ID
+ && set_list[i] == NULL)
+ idx = i;
+ if (set_list[i] != NULL && STREQ(set_list[i]->name, name))
+ exit_error(PARAMETER_PROBLEM,
+ "Set %s is already defined, cannot be restored",
+ name);
+ }
+
+ if (idx == IP_SET_INVALID_ID)
+ exit_error(PARAMETER_PROBLEM,
+ "Set %s cannot be restored, "
+ "max number of set %u reached",
+ name, max_sets);
+
+ return idx;
+}
+
+/*
+ * 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 = 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 %zu %zu %u %u", name, settype->typename,
+ restore_offset, sizeof(struct ip_set_restore),
+ settype->create_size, restore_size);
+
+ /* Sanity checking */
+ if (restore_offset
+ + ALIGNED(sizeof(struct ip_set_restore))
+ + ALIGNED(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 */
+
+ restore_offset += ALIGNED(sizeof(struct ip_set_restore));
+ memcpy(restore_data + restore_offset, settype->data, settype->create_size);
+
+ restore_offset += ALIGNED(settype->create_size);
+ DP("restore_offset: %zu", restore_offset);
+
+ /* 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 = 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 = 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 *idx,
+ 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;
+ socklen_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 = 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 *));
+ *idx = req_max_sets.set.index;
+
+ if (req_max_sets.sets == 0)
+ /* No sets in kernel */
+ return 0;
+
+ /* Get setnames */
+ size = req_size = ALIGNED(sizeof(struct ip_set_req_setnames))
+ + req_max_sets.sets * ALIGNED(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 = *idx;
+
+ 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 = ALIGNED(sizeof(struct ip_set_req_setnames));
+ while (size + ALIGNED(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 += ALIGNED(sizeof(struct ip_set_name_list));
+ }
+ /* Size to get set members */
+ size = ((struct ip_set_req_setnames *)data)->size;
+ free(data);
+
+ return size;
+}
+
+/*
+ * Save operation
+ */
+static size_t save_set(void *data, 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 %zu (%zu/%u/%u), len %zu", offset,
+ sizeof(struct ip_set_save),
+ set_save->header_size, set_save->members_size,
+ len);
+ if (offset + ALIGNED(sizeof(struct ip_set_save)) > len
+ || offset + ALIGNED(sizeof(struct ip_set_save))
+ + set_save->header_size + set_save->members_size > len)
+ exit_error(OTHER_PROBLEM,
+ "Save operation failed, try again later.");
+
+ DP("index: %u", set_save->index);
+ if (set_save->index == IP_SET_INVALID_ID) {
+ /* Marker */
+ return ALIGNED(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 = ALIGNED(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,
+ DONT_ALIGN);
+
+ return (used + set_save->members_size);
+}
+
+static int try_save_sets(const char name[IP_SET_MAXNAMELEN])
+{
+ void *data = NULL;
+ socklen_t size, req_size = 0;
+ ip_set_id_t idx;
+ int res = 0;
+ time_t now = time(NULL);
+
+ /* Load set_list from kernel */
+ size = load_set_list(name, &idx,
+ IP_SET_OP_SAVE_SIZE, CMD_SAVE);
+
+ if (size) {
+ /* Get sets and print them */
+ /* Take into account marker */
+ req_size = (size += ALIGNED(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 = idx;
+ res = kernel_getfrom_handleerrno(CMD_SAVE, data, &size);
+
+ if (res != 0 || size != req_size) {
+ DP("Try again: res: %i, size %u, req_size: %u",
+ res, 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);
+ size += save_set(data, size, req_size);
+ }
+ 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(unsigned 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) < (int)(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], __tmpdir[] = "/tmp";
+ char *tmpdir = NULL;
+ char *filename;
+ int fd;
+ FILE *file;
+
+ if (!(tmpdir = getenv("TMPDIR")) && !(tmpdir = getenv("TMP")))
+ tmpdir = __tmpdir;
+ 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 first_pass, i;
+ struct settype *settype = NULL;
+ struct ip_set_req_setnames *header;
+ ip_set_id_t idx;
+ 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, &idx,
+ IP_SET_OP_LIST_SIZE, CMD_RESTORE);
+
+ restore_line = 0;
+ restore_size = ALIGNED(sizeof(struct ip_set_req_setnames)); /* header */
+ DP("restore_size: %u", restore_size);
+ /* First pass: calculate required amount of data */
+ while (fgets(buffer, sizeof(buffer), in)) {
+ restore_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",
+ restore_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",
+ restore_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",
+ restore_line);
+ settype = check_set_typename(ptr);
+ restore_size += ALIGNED(sizeof(struct ip_set_restore))
+ + ALIGNED(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, restore_line);
+ restore_size += ALIGNED(settype->adt_size);
+ DP("restore_size (A): %u", restore_size);
+ break;
+ }
+ default: {
+ exit_error(PARAMETER_PROBLEM,
+ "Unrecognized restore command in line %u\n",
+ restore_line);
+ }
+ } /* end of switch */
+ }
+ /* Sanity checking */
+ if (!restore)
+ exit_error(PARAMETER_PROBLEM,
+ "Missing COMMIT line\n");
+ restore_size += ALIGNED(sizeof(struct ip_set_restore)); /* marker */
+ 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 = ALIGNED(sizeof(struct ip_set_req_setnames));
+
+ /* Rewind to scan the file again */
+ fseek(in, 0L, SEEK_SET);
+ first_pass = restore_line;
+ restore_line = 0;
+
+ /* Initialize newargv/newargc */
+ newargv[newargc++] = ipset_strdup(argv0);
+
+ /* Second pass: build up restore request */
+ while (fgets(buffer, sizeof(buffer), in)) {
+ restore_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(restore_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 (restore_size == (restore_offset + ALIGNED(sizeof(struct ip_set_restore)))) {
+ /* No bindings */
+ struct ip_set_restore *marker =
+ (struct ip_set_restore *) (restore_data + restore_offset);
+
+ marker->index = IP_SET_INVALID_ID;
+ marker->header_size = marker->members_size = 0;
+ restore_offset += ALIGNED(sizeof(struct ip_set_restore));
+ DP("restore marker, restore_offset: %zu", restore_offset);
+ }
+ 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;
+ socklen_t size;
+
+ DP("%s", name);
+
+ check_protocolversion();
+
+ req_adt_get.op = IP_SET_OP_ADT_GET;
+ req_adt_get.version = protocol_version;
+ strcpy(req_adt_get.set.name, name);
+ size = sizeof(struct ip_set_req_adt_get);
+
+ kernel_getfrom(CMD_ADT_GET, (void *) &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 = ALIGNED(sizeof(struct ip_set_req_adt)) + set->settype->adt_size ;
+ DP("alloc size %zu", 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 + ALIGNED(sizeof(struct ip_set_req_adt)),
+ set->settype->data, set->settype->adt_size);
+
+ if (kernel_sendto_handleerrno(cmd, 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 UNUSED)
+{
+ DP("%s %s", set->name, adt);
+ /* Sanity checking */
+ if (restore_offset + ALIGNED(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 += ALIGNED(set->settype->adt_size);
+ restore_offset += ALIGNED(set->settype->adt_size);
+
+ DP("restore_offset: %zu", restore_offset);
+}
+
+/*
+ * Print operation
+ */
+
+/* Help function to set_list() */
+static size_t print_set(void *data, unsigned options)
+{
+ struct ip_set_list *setlist = data;
+ struct set *set = set_list[setlist->index];
+ struct settype *settype = set->settype;
+ size_t offset;
+
+ /* Pretty print the set */
+ DP("header size: %u, members size: %u",
+ setlist->header_size, setlist->members_size);
+ printf("Name: %s\n", set->name);
+ printf("Type: %s\n", settype->typename);
+ printf("References: %d\n", setlist->ref);
+
+ /* Init header */
+ offset = ALIGNED(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;
+ DP("Aligned: %u, offset: %zu, members_size %u\n", !DONT_ALIGN, offset,
+ setlist->members_size);
+ if (options & OPT_SORTED)
+ settype->printips_sorted(set, data + offset,
+ setlist->members_size, options,
+ DONT_ALIGN);
+ else
+ settype->printips(set, data + offset,
+ setlist->members_size, options,
+ DONT_ALIGN);
+
+ printf("\n"); /* One newline between sets */
+
+ return (offset + setlist->members_size);
+}
+
+static int try_list_sets(const char name[IP_SET_MAXNAMELEN],
+ unsigned options)
+{
+ void *data = NULL;
+ ip_set_id_t idx;
+ socklen_t size, req_size;
+ int res = 0;
+
+ /* Default is numeric listing */
+ if (!(options & (OPT_RESOLVE|OPT_NUMERIC)))
+ options |= OPT_NUMERIC;
+
+ DP("%s", name);
+ /* Load set_list from kernel */
+ size = req_size = load_set_list(name, &idx,
+ 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 = idx;
+ 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)
+{
+ printf("%s v%s\n\n"
+ "Usage: %s -N new-set settype [options]\n"
+ " %s -[XFLSH] [set] [options]\n"
+ " %s -[EW] from-set to-set\n"
+ " %s -[ADT] set IP\n"
+ " %s -R\n"
+ " %s -v\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"
+ " --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 (default)\n"
+ " --resolve -r Try to resolve addresses in a -L\n"
+ " --quiet -q Suppress any output to stdout and stderr.\n");
+#ifdef IPSET_DEBUG
+ printf(" --debug -z Enable debugging\n\n");
+#else
+ printf("\n");
+#endif
+
+ if (settype != NULL) {
+ printf("Type '%s' specific:\n", settype->typename);
+ settype->usage();
+ }
+}
+
+static int find_cmd(int 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(int command,
+ const char *name,
+ char *adt,
+ struct set **set,
+ struct settype **settype)
+{
+ int res = 0;
+
+ *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);
+
+ res = (*settype)->adt_parser(command, adt, (*settype)->data);
+
+ return res;
+}
+
+/* Main worker function */
+int parse_commandline(int argc, char *argv[])
+{
+ int res = 0;
+ int 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 */
+ struct set *set = NULL; /* -A, -D, -T */
+ 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':
+ case 'v': { /* Version */
+ printf("%s v%s, protocol version %u.\n",
+ program_name, program_version,
+ IP_SET_PROTOCOL_VERSION);
+ check_protocolversion();
+ printf("Kernel module protocol version %u.\n",
+ protocol_version);
+ exit(0);
+ }
+
+ case 'N':{ /* Create: -N name typename options */
+ set_command(&command, CMD_CREATE);
+
+ name = check_set_name(optarg);
+
+ /* Protect reserved names */
+ 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 */
+ 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]);
+
+ res = 0;
+ break;
+ }
+
+ /* options */
+
+ case 'n':
+ add_option(&options, OPT_NUMERIC);
+ break;
+
+ case 'r':
+ if (!(options & OPT_NUMERIC))
+ add_option(&options, OPT_RESOLVE);
+ 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 1: /* non option */
+ printf("Bad argument `%s'\n", optarg);
+ exit_tryhelp(PARAMETER_PROBLEM);
+ 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]);
+
+ res = 0;
+ }
+
+ 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));
+
+ check_protocolversion();
+
+ 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:
+ res = set_adtip(set, adt, IP_SET_OP_TEST_IP, CMD_TEST);
+ 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/src/ipset.h b/src/ipset.h
new file mode 100644
index 0000000..c49418d
--- /dev/null
+++ b/src/ipset.h
@@ -0,0 +1,200 @@
+#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> /* struct option */
+#include <stdint.h>
+#include <sys/types.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_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 */
+ u_int32_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 */
+ const struct option *create_opts;
+
+ /*
+ * Add/del/test IP
+ */
+
+ /* Size of data. Will be sent to kernel */
+ u_int32_t adt_size;
+
+ /* Function which parses command options */
+ ip_set_ip_t (*adt_parser) (int cmd, const char *optarg, void *data);
+
+ /*
+ * Printing
+ */
+
+ /* Size of header. */
+ u_int32_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, u_int32_t len,
+ unsigned options, char dont_align);
+
+ /* Pretty print all IPs sorted */
+ void (*printips_sorted) (struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align);
+
+ /* 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, u_int32_t len,
+ unsigned options, char dont_align);
+
+ /* Print usage */
+ void (*usage) (void);
+
+ /* Internal data */
+ void *header;
+ void *data;
+ 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(int status, const 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);
+
+extern struct set *set_find_byname(const char *name);
+extern struct set *set_find_byid(ip_set_id_t id);
+
+extern unsigned warn_once;
+
+#define BITS_PER_LONG (8*sizeof(ip_set_ip_t))
+#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
+
+static inline int test_bit(int nr, const ip_set_ip_t *addr)
+{
+ return 1 & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
+}
+
+#define UNUSED __attribute__ ((unused))
+#define CONSTRUCTOR(module) \
+void __attribute__ ((constructor)) module##_init(void); \
+void module##_init(void)
+
+#endif /* __IPSET_H */
diff --git a/src/ipset_iphash.c b/src/ipset_iphash.c
new file mode 100644
index 0000000..0bc5bd1
--- /dev/null
+++ b/src/ipset_iphash.c
@@ -0,0 +1,279 @@
+/* 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 <limits.h> /* UINT_MAX */
+#include <stdio.h> /* *printf */
+#include <string.h> /* mem* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_iphash.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. */
+static void
+iphash_create_init(void *data)
+{
+ struct ip_set_req_iphash_create *mydata = 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 */
+static int
+iphash_create_parse(int c, char *argv[] UNUSED, 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. */
+static void
+iphash_create_final(void *data UNUSED, unsigned int flags UNUSED)
+{
+}
+
+/* Create commandline options */
+static const struct option create_opts[] = {
+ {.name = "hashsize", .has_arg = required_argument, .val = '1'},
+ {.name = "probes", .has_arg = required_argument, .val = '2'},
+ {.name = "resize", .has_arg = required_argument, .val = '3'},
+ {.name = "netmask", .has_arg = required_argument, .val = '4'},
+ {NULL},
+};
+
+/* Add, del, test parser */
+static ip_set_ip_t
+iphash_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_iphash *mydata = data;
+
+ parse_ip(arg, &mydata->ip);
+ if (!mydata->ip)
+ exit_error(PARAMETER_PROBLEM,
+ "Zero valued IP address `%s' specified", arg);
+
+ return mydata->ip;
+};
+
+/*
+ * Print and save
+ */
+
+static void
+iphash_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_iphash_create *header = data;
+ struct ip_set_iphash *map = 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;
+}
+
+static 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;
+}
+
+static void
+iphash_printheader(struct set *set, unsigned options UNUSED)
+{
+ struct ip_set_iphash *mysetdata = 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));
+}
+
+static void
+iphash_printips(struct set *set UNUSED, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ size_t offset = 0;
+ ip_set_ip_t *ip;
+
+ while (offset < len) {
+ ip = data + offset;
+ printf("%s\n", ip_tostring(*ip, options));
+ offset += IPSET_VALIGN(sizeof(ip_set_ip_t), dont_align);
+ }
+}
+
+static void
+iphash_saveheader(struct set *set, unsigned options UNUSED)
+{
+ struct ip_set_iphash *mysetdata = 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 */
+static void
+iphash_saveips(struct set *set UNUSED, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ size_t offset = 0;
+ ip_set_ip_t *ip;
+
+ while (offset < len) {
+ ip = data + offset;
+ printf("-A %s %s\n", set->name, ip_tostring(*ip, options));
+ offset += IPSET_VALIGN(sizeof(ip_set_ip_t), dont_align);
+ }
+}
+
+static void
+iphash_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 = iphash_create_init,
+ .create_parse = iphash_create_parse,
+ .create_final = iphash_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_iphash),
+ .adt_parser = iphash_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_iphash),
+ .initheader = iphash_initheader,
+ .printheader = iphash_printheader,
+ .printips = iphash_printips,
+ .printips_sorted = iphash_printips,
+ .saveheader = iphash_saveheader,
+ .saveips = iphash_saveips,
+
+ .usage = iphash_usage,
+};
+
+CONSTRUCTOR(iphash)
+{
+ settype_register(&settype_iphash);
+
+}
diff --git a/src/ipset_ipmap.c b/src/ipset_ipmap.c
new file mode 100644
index 0000000..87b8e69
--- /dev/null
+++ b/src/ipset_ipmap.c
@@ -0,0 +1,376 @@
+/* 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> /* *printf */
+#include <string.h> /* mem* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_ipmap.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. */
+static void
+ipmap_create_init(void *data)
+{
+ struct ip_set_req_ipmap_create *mydata = data;
+
+ DP("create INIT");
+ mydata->netmask = 0xFFFFFFFF;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+ipmap_create_parse(int c, char *argv[] UNUSED, void *data, unsigned *flags)
+{
+ struct ip_set_req_ipmap_create *mydata = 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;
+}
+
+/* Final check; exit if not ok. */
+static void
+ipmap_create_final(void *data, unsigned int flags)
+{
+ struct ip_set_req_ipmap_create *mydata = data;
+ ip_set_ip_t range;
+
+ 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)) {
+ exit_error(PARAMETER_PROBLEM,
+ "You have to define a full network with --from"
+ " and --to if you specify the --network option\n");
+ }
+ netmask_bits = mask_to_bits(mydata->netmask);
+ if (netmask_bits <= mask_bits) {
+ exit_error(PARAMETER_PROBLEM,
+ "%d netmask specifies larger or equal netblock than the network itself\n");
+ }
+ range = (1<<(netmask_bits - mask_bits)) - 1;
+ } else {
+ range = mydata->to - mydata->from;
+ }
+ if (range > MAX_RANGE)
+ exit_error(PARAMETER_PROBLEM,
+ "Range too large. Max is %d IPs in range\n",
+ MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static const struct option create_opts[] = {
+ {.name = "from", .has_arg = required_argument, .val = '1'},
+ {.name = "to", .has_arg = required_argument, .val = '2'},
+ {.name = "network", .has_arg = required_argument, .val = '3'},
+ {.name = "netmask", .has_arg = required_argument, .val = '4'},
+ {NULL},
+};
+
+/* Add, del, test parser */
+static ip_set_ip_t
+ipmap_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_ipmap *mydata = data;
+
+ DP("ipmap: %p %p", arg, data);
+
+ parse_ip(arg, &mydata->ip);
+ DP("%s", ip_tostring_numeric(mydata->ip));
+
+ return 1;
+}
+
+/*
+ * Print and save
+ */
+
+static void
+ipmap_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_ipmap_create *header = data;
+ struct ip_set_ipmap *map = 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: %d %d", mask_bits, netmask_bits);
+ map->hosts = 2 << (32 - netmask_bits - 1);
+ map->sizeid = 2 << (netmask_bits - mask_bits - 1);
+ }
+
+ DP("%d %d", map->hosts, map->sizeid );
+}
+
+static void
+ipmap_printheader(struct set *set, unsigned options)
+{
+ struct ip_set_ipmap *mysetdata = 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));
+}
+
+static inline void
+__ipmap_printips_sorted(struct set *set, void *data,
+ u_int32_t len UNUSED, unsigned options)
+{
+ struct ip_set_ipmap *mysetdata = 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));
+}
+
+static void
+ipmap_printips_sorted(struct set *set, void *data,
+ u_int32_t len, unsigned options,
+ char dont_align)
+{
+ ip_set_ip_t *ip;
+ size_t offset = 0;
+
+ if (dont_align)
+ return __ipmap_printips_sorted(set, data, len, options);
+
+ while (offset < len) {
+ DP("offset: %zu, len %u\n", offset, len);
+ ip = data + offset;
+ printf("%s\n", ip_tostring(*ip, options));
+ offset += IPSET_ALIGN(sizeof(ip_set_ip_t));
+ }
+}
+
+static void
+ipmap_saveheader(struct set *set, unsigned options)
+{
+ struct ip_set_ipmap *mysetdata = 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));
+}
+
+static inline void
+__ipmap_saveips(struct set *set, void *data, u_int32_t len UNUSED,
+ unsigned options)
+{
+ struct ip_set_ipmap *mysetdata = 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));
+}
+
+static void
+ipmap_saveips(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ ip_set_ip_t *ip;
+ size_t offset = 0;
+
+ if (dont_align)
+ return __ipmap_saveips(set, data, len, options);
+
+ while (offset < len) {
+ ip = data + offset;
+ printf("-A %s %s\n", set->name, ip_tostring(*ip, options));
+ offset += IPSET_ALIGN(sizeof(ip_set_ip_t));
+ }
+}
+
+static void
+ipmap_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 = ipmap_create_init,
+ .create_parse = ipmap_create_parse,
+ .create_final = ipmap_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_ipmap),
+ .adt_parser = ipmap_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_ipmap),
+ .initheader = ipmap_initheader,
+ .printheader = ipmap_printheader,
+ .printips = ipmap_printips_sorted,
+ .printips_sorted = ipmap_printips_sorted,
+ .saveheader = ipmap_saveheader,
+ .saveips = ipmap_saveips,
+
+ .usage = ipmap_usage,
+};
+
+CONSTRUCTOR(ipmap)
+{
+ settype_register(&settype_ipmap);
+
+}
diff --git a/src/ipset_ipporthash.c b/src/ipset_ipporthash.c
new file mode 100644
index 0000000..9249b2a
--- /dev/null
+++ b/src/ipset_ipporthash.c
@@ -0,0 +1,350 @@
+/* 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 <limits.h> /* UINT_MAX */
+#include <stdio.h> /* *printf */
+#include <string.h> /* mem*, str* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_ipporthash.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. */
+static void
+ipporthash_create_init(void *data)
+{
+ struct ip_set_req_ipporthash_create *mydata = 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 */
+static int
+ipporthash_create_parse(int c, char *argv[] UNUSED, void *data,
+ unsigned *flags)
+{
+ struct ip_set_req_ipporthash_create *mydata = 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. */
+static void
+ipporthash_create_final(void *data, unsigned int flags)
+{
+ struct ip_set_req_ipporthash_create *mydata = 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 too large. Max is %d IPs in range\n",
+ MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static const struct option create_opts[] = {
+ {.name = "hashsize", .has_arg = required_argument, .val = '1'},
+ {.name = "probes", .has_arg = required_argument, .val = '2'},
+ {.name = "resize", .has_arg = required_argument, .val = '3'},
+ {.name = "from", .has_arg = required_argument, .val = '4'},
+ {.name = "to", .has_arg = required_argument, .val = '5'},
+ {.name = "network", .has_arg = required_argument, .val = '6'},
+ {NULL},
+};
+
+/* Add, del, test parser */
+static ip_set_ip_t
+ipporthash_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_ipporthash *mydata = data;
+ char *saved = ipset_strdup(arg);
+ char *ptr, *tmp = saved;
+
+ DP("ipporthash: %p %p", arg, data);
+
+ if (((ptr = strchr(tmp, ':')) || (ptr = strchr(tmp, '%'))) && ++warn_once == 1)
+ fprintf(stderr, "Warning: please use ',' separator token between ip,port.\n"
+ "Next release won't support old separator tokens.\n");
+
+ 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");
+
+ if (!(mydata->ip || mydata->port))
+ exit_error(PARAMETER_PROBLEM,
+ "Zero valued IP address and port `%s' specified", arg);
+ ipset_free(saved);
+ return 1;
+};
+
+/*
+ * Print and save
+ */
+
+static void
+ipporthash_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_ipporthash_create *header = data;
+ struct ip_set_ipporthash *map = 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;
+}
+
+static void
+ipporthash_printheader(struct set *set, unsigned options)
+{
+ struct ip_set_ipporthash *mysetdata = 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);
+}
+
+static void
+ipporthash_printips(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ struct ip_set_ipporthash *mysetdata = set->settype->header;
+ size_t offset = 0;
+ ip_set_ip_t *ipptr, ip;
+ uint16_t port;
+
+ while (offset < len) {
+ ipptr = data + offset;
+ ip = (*ipptr>>16) + mysetdata->first_ip;
+ port = (uint16_t) *ipptr;
+ printf("%s,%s\n",
+ ip_tostring(ip, options),
+ port_tostring(port, options));
+ offset += IPSET_VALIGN(sizeof(ip_set_ip_t), dont_align);
+ }
+}
+
+static void
+ipporthash_saveheader(struct set *set, unsigned options)
+{
+ struct ip_set_ipporthash *mysetdata = 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 */
+static void
+ipporthash_saveips(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ struct ip_set_ipporthash *mysetdata = set->settype->header;
+ size_t offset = 0;
+ ip_set_ip_t *ipptr, ip;
+ uint16_t port;
+
+ while (offset < len) {
+ ipptr = data + offset;
+ 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 += IPSET_VALIGN(sizeof(ip_set_ip_t), dont_align);
+ }
+}
+
+static void
+ipporthash_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 = ipporthash_create_init,
+ .create_parse = ipporthash_create_parse,
+ .create_final = ipporthash_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_ipporthash),
+ .adt_parser = ipporthash_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_ipporthash),
+ .initheader = ipporthash_initheader,
+ .printheader = ipporthash_printheader,
+ .printips = ipporthash_printips,
+ .printips_sorted = ipporthash_printips,
+ .saveheader = ipporthash_saveheader,
+ .saveips = ipporthash_saveips,
+
+ .usage = ipporthash_usage,
+};
+
+CONSTRUCTOR(ipporthash)
+{
+ settype_register(&settype_ipporthash);
+
+}
diff --git a/src/ipset_ipportiphash.c b/src/ipset_ipportiphash.c
new file mode 100644
index 0000000..49861bf
--- /dev/null
+++ b/src/ipset_ipportiphash.c
@@ -0,0 +1,361 @@
+/* Copyright 2008 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 <limits.h> /* UINT_MAX */
+#include <stdio.h> /* *printf */
+#include <string.h> /* mem*, str* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_ipportiphash.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. */
+static void
+ipportiphash_create_init(void *data)
+{
+ struct ip_set_req_ipportiphash_create *mydata = 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 */
+static int
+ipportiphash_create_parse(int c, char *argv[] UNUSED, void *data,
+ unsigned *flags)
+{
+ struct ip_set_req_ipportiphash_create *mydata = 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. */
+static void
+ipportiphash_create_final(void *data, unsigned int flags)
+{
+ struct ip_set_req_ipportiphash_create *mydata = 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 too large. Max is %d IPs in range\n",
+ MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static const struct option create_opts[] = {
+ {.name = "hashsize", .has_arg = required_argument, .val = '1'},
+ {.name = "probes", .has_arg = required_argument, .val = '2'},
+ {.name = "resize", .has_arg = required_argument, .val = '3'},
+ {.name = "from", .has_arg = required_argument, .val = '4'},
+ {.name = "to", .has_arg = required_argument, .val = '5'},
+ {.name = "network", .has_arg = required_argument, .val = '6'},
+ {NULL},
+};
+
+/* Add, del, test parser */
+static ip_set_ip_t
+ipportiphash_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_ipportiphash *mydata = data;
+ char *saved = ipset_strdup(arg);
+ char *ptr, *tmp = saved;
+
+ DP("ipportiphash: %p %p", arg, data);
+
+ if (((ptr = strchr(tmp, ':')) || (ptr = strchr(tmp, '%'))) && ++warn_once == 1)
+ fprintf(stderr, "Warning: please use ',' separator token between ip,port,ip.\n"
+ "Next release won't support old separator tokens.\n");
+
+ ptr = strsep(&tmp, ":%,");
+ parse_ip(ptr, &mydata->ip);
+
+ if (!tmp)
+ exit_error(PARAMETER_PROBLEM,
+ "IP address, port and IP address must be specified: ip,port,ip");
+
+ ptr = strsep(&tmp, ":%,");
+ parse_port(ptr, &mydata->port);
+ if (tmp)
+ parse_ip(tmp, &mydata->ip1);
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "IP address, port and IP address must be specified: ip,port,ip");
+ if (!(mydata->ip || mydata->port || mydata->ip1))
+ exit_error(PARAMETER_PROBLEM,
+ "Zero valued IP address, port and IP address `%s' specified", arg);
+ ipset_free(saved);
+ return 1;
+};
+
+/*
+ * Print and save
+ */
+
+static void
+ipportiphash_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_ipportiphash_create *header = data;
+ struct ip_set_ipportiphash *map = set->settype->header;
+
+ memset(map, 0, sizeof(struct ip_set_ipportiphash));
+ map->hashsize = header->hashsize;
+ map->probes = header->probes;
+ map->resize = header->resize;
+ map->first_ip = header->from;
+ map->last_ip = header->to;
+}
+
+static void
+ipportiphash_printheader(struct set *set, unsigned options)
+{
+ struct ip_set_ipportiphash *mysetdata = 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);
+}
+
+static void
+ipportiphash_printips(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ struct ip_set_ipportiphash *mysetdata = set->settype->header;
+ size_t offset = 0;
+ struct ipportip *ipptr;
+ ip_set_ip_t ip;
+ uint16_t port;
+
+ while (offset < len) {
+ ipptr = data + offset;
+ ip = (ipptr->ip>>16) + mysetdata->first_ip;
+ port = (uint16_t) ipptr->ip;
+ printf("%s,%s,",
+ ip_tostring(ip, options),
+ port_tostring(port, options));
+ printf("%s\n",
+ ip_tostring(ipptr->ip1, options));
+ offset += IPSET_VALIGN(sizeof(struct ipportip), dont_align);
+ }
+}
+
+static void
+ipportiphash_saveheader(struct set *set, unsigned options)
+{
+ struct ip_set_ipportiphash *mysetdata = 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 */
+static void
+ipportiphash_saveips(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ struct ip_set_ipportiphash *mysetdata = set->settype->header;
+ size_t offset = 0;
+ struct ipportip *ipptr;
+ ip_set_ip_t ip;
+ uint16_t port;
+
+ while (offset < len) {
+ ipptr = data + offset;
+ ip = (ipptr->ip>>16) + mysetdata->first_ip;
+ port = (uint16_t) ipptr->ip;
+ printf("-A %s %s,%s,", set->name,
+ ip_tostring(ip, options),
+ port_tostring(port, options));
+ printf("%s\n",
+ ip_tostring(ipptr->ip1, options));
+ offset += IPSET_VALIGN(sizeof(struct ipportip), dont_align);
+ }
+}
+
+static void
+ipportiphash_usage(void)
+{
+ printf
+ ("-N set ipportiphash --from IP --to IP\n"
+ " [--hashsize hashsize] [--probes probes ] [--resize resize]\n"
+ "-N set ipportiphash --network IP/mask\n"
+ " [--hashsize hashsize] [--probes probes ] [--resize resize]\n"
+ "-A set IP,port,IP\n"
+ "-D set IP,port,IP\n"
+ "-T set IP,port,IP\n");
+}
+
+static struct settype settype_ipportiphash = {
+ .typename = SETTYPE_NAME,
+ .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+ /* Create */
+ .create_size = sizeof(struct ip_set_req_ipportiphash_create),
+ .create_init = ipportiphash_create_init,
+ .create_parse = ipportiphash_create_parse,
+ .create_final = ipportiphash_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_ipportiphash),
+ .adt_parser = ipportiphash_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_ipportiphash),
+ .initheader = ipportiphash_initheader,
+ .printheader = ipportiphash_printheader,
+ .printips = ipportiphash_printips,
+ .printips_sorted = ipportiphash_printips,
+ .saveheader = ipportiphash_saveheader,
+ .saveips = ipportiphash_saveips,
+
+ .usage = ipportiphash_usage,
+};
+
+CONSTRUCTOR(ipportiphash)
+{
+ settype_register(&settype_ipportiphash);
+
+}
diff --git a/src/ipset_ipportnethash.c b/src/ipset_ipportnethash.c
new file mode 100644
index 0000000..3a60bf1
--- /dev/null
+++ b/src/ipset_ipportnethash.c
@@ -0,0 +1,426 @@
+/* Copyright 2008 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 <limits.h> /* UINT_MAX */
+#include <stdio.h> /* *printf */
+#include <string.h> /* mem*, str* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_ipportnethash.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. */
+static void
+ipportnethash_create_init(void *data)
+{
+ struct ip_set_req_ipportnethash_create *mydata = 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 */
+static int
+ipportnethash_create_parse(int c, char *argv[] UNUSED, void *data,
+ unsigned *flags)
+{
+ struct ip_set_req_ipportnethash_create *mydata = 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. */
+static void
+ipportnethash_create_final(void *data, unsigned int flags)
+{
+ struct ip_set_req_ipportnethash_create *mydata = 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 too large. Max is %d IPs in range\n",
+ MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static const struct option create_opts[] = {
+ {.name = "hashsize", .has_arg = required_argument, .val = '1'},
+ {.name = "probes", .has_arg = required_argument, .val = '2'},
+ {.name = "resize", .has_arg = required_argument, .val = '3'},
+ {.name = "from", .has_arg = required_argument, .val = '4'},
+ {.name = "to", .has_arg = required_argument, .val = '5'},
+ {.name = "network", .has_arg = required_argument, .val = '6'},
+ {NULL},
+};
+
+/* Add, del, test parser */
+static ip_set_ip_t
+ipportnethash_adt_parser(int cmd, const char *arg, void *data)
+{
+ struct ip_set_req_ipportnethash *mydata = data;
+ char *saved = ipset_strdup(arg);
+ char *ptr, *tmp = saved;
+ ip_set_ip_t cidr;
+
+ DP("ipportnethash: %p %p", arg, data);
+
+ if (((ptr = strchr(tmp, ':')) || (ptr = strchr(tmp, '%'))) && ++warn_once == 1)
+ fprintf(stderr, "Warning: please use ',' separator token between ip,port,net.\n"
+ "Next release won't support old separator tokens.\n");
+
+ ptr = strsep(&tmp, ":%,");
+ parse_ip(ptr, &mydata->ip);
+ if (!tmp)
+ exit_error(PARAMETER_PROBLEM,
+ "IP address, port and network address must be specified: ip,port,net");
+
+ ptr = strsep(&tmp, ":%,");
+ parse_port(ptr, &mydata->port);
+ if (!tmp)
+ exit_error(PARAMETER_PROBLEM,
+ "IP address, port and network address must be specified: ip,port,net");
+
+ ptr = strsep(&tmp, "/");
+ if (tmp == NULL)
+ if (cmd == CMD_TEST)
+ cidr = 32;
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "Missing /cidr from `%s'", arg);
+ else
+ if (string_to_number(tmp, 1, 31, &cidr))
+ exit_error(PARAMETER_PROBLEM,
+ "Out of range cidr `%s' specified", arg);
+
+ mydata->cidr = cidr;
+
+ parse_ip(ptr, &mydata->ip1);
+ ipset_free(saved);
+ return 1;
+};
+
+/*
+ * Print and save
+ */
+
+static void
+ipportnethash_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_ipportnethash_create *header = data;
+ struct ip_set_ipportnethash *map = set->settype->header;
+
+ memset(map, 0, sizeof(struct ip_set_ipportnethash));
+ map->hashsize = header->hashsize;
+ map->probes = header->probes;
+ map->resize = header->resize;
+ map->first_ip = header->from;
+ map->last_ip = header->to;
+}
+
+static void
+ipportnethash_printheader(struct set *set, unsigned options)
+{
+ struct ip_set_ipportnethash *mysetdata = 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);
+}
+
+static char buf[20];
+
+static char *
+unpack_ip_tostring(ip_set_ip_t ip, unsigned options UNUSED)
+{
+ 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), 0), buf);
+ return buf;
+}
+
+static void
+ipportnethash_printips(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ struct ip_set_ipportnethash *mysetdata = set->settype->header;
+ size_t offset = 0;
+ struct ipportip *ipptr;
+ ip_set_ip_t ip;
+ uint16_t port;
+
+ while (offset < len) {
+ ipptr = data + offset;
+ ip = (ipptr->ip>>16) + mysetdata->first_ip;
+ port = (uint16_t) ipptr->ip;
+ printf("%s,%s,",
+ ip_tostring(ip, options),
+ port_tostring(port, options));
+ printf("%s\n",
+ unpack_ip_tostring(ipptr->ip1, options));
+ offset += IPSET_VALIGN(sizeof(struct ipportip), dont_align);
+ }
+}
+
+static void
+ipportnethash_saveheader(struct set *set, unsigned options)
+{
+ struct ip_set_ipportnethash *mysetdata = 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 */
+static void
+ipportnethash_saveips(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ struct ip_set_ipportnethash *mysetdata = set->settype->header;
+ size_t offset = 0;
+ struct ipportip *ipptr;
+ ip_set_ip_t ip;
+ uint16_t port;
+
+ while (offset < len) {
+ ipptr = data + offset;
+ ip = (ipptr->ip>>16) + mysetdata->first_ip;
+ port = (uint16_t) ipptr->ip;
+ printf("-A %s %s,%s,", set->name,
+ ip_tostring(ip, options),
+ port_tostring(port, options));
+ printf("%s\n",
+ unpack_ip_tostring(ipptr->ip, options));
+ offset += IPSET_VALIGN(sizeof(struct ipportip), dont_align);
+ }
+}
+
+static void
+ipportnethash_usage(void)
+{
+ printf
+ ("-N set ipportnethash --from IP --to IP\n"
+ " [--hashsize hashsize] [--probes probes ] [--resize resize]\n"
+ "-N set ipportnethash --network IP/mask\n"
+ " [--hashsize hashsize] [--probes probes ] [--resize resize]\n"
+ "-A set IP,port,IP/net\n"
+ "-D set IP,port,IP/net\n"
+ "-T set IP,port,IP[/net]\n");
+}
+
+static struct settype settype_ipportnethash = {
+ .typename = SETTYPE_NAME,
+ .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+ /* Create */
+ .create_size = sizeof(struct ip_set_req_ipportnethash_create),
+ .create_init = ipportnethash_create_init,
+ .create_parse = ipportnethash_create_parse,
+ .create_final = ipportnethash_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_ipportnethash),
+ .adt_parser = ipportnethash_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_ipportnethash),
+ .initheader = ipportnethash_initheader,
+ .printheader = ipportnethash_printheader,
+ .printips = ipportnethash_printips,
+ .printips_sorted = ipportnethash_printips,
+ .saveheader = ipportnethash_saveheader,
+ .saveips = ipportnethash_saveips,
+
+ .usage = ipportnethash_usage,
+};
+
+CONSTRUCTOR(ipportnethash)
+{
+ settype_register(&settype_ipportnethash);
+
+}
diff --git a/src/ipset_iptree.c b/src/ipset_iptree.c
new file mode 100644
index 0000000..508e67a
--- /dev/null
+++ b/src/ipset_iptree.c
@@ -0,0 +1,224 @@
+/* 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 <limits.h> /* UINT_MAX */
+#include <stdio.h> /* *printf */
+#include <string.h> /* mem* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_iptree.h>
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_TIMEOUT 0x01U
+
+/* Initialize the create. */
+static void
+iptree_create_init(void *data)
+{
+ struct ip_set_req_iptree_create *mydata = data;
+
+ DP("create INIT");
+ mydata->timeout = 0;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+iptree_create_parse(int c, char *argv[] UNUSED, void *data, unsigned *flags)
+{
+ struct ip_set_req_iptree_create *mydata = 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. */
+static void
+iptree_create_final(void *data UNUSED, unsigned int flags UNUSED)
+{
+}
+
+/* Create commandline options */
+static const struct option create_opts[] = {
+ {.name = "timeout", .has_arg = required_argument, .val = '1'},
+ {NULL},
+};
+
+/* Add, del, test parser */
+static ip_set_ip_t
+iptree_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_iptree *mydata = data;
+ char *saved = ipset_strdup(arg);
+ char *ptr, *tmp = saved;
+
+ DP("iptree: %p %p", arg, data);
+
+ if (((ptr = strchr(tmp, ':')) || (ptr = strchr(tmp, '%'))) && ++warn_once == 1)
+ fprintf(stderr, "Warning: please use ',' separator token between ip,timeout.\n"
+ "Next release won't support old separator tokens.\n");
+
+ ptr = strsep(&tmp, ":%,");
+ parse_ip(ptr, &mydata->ip);
+
+ if (tmp)
+ string_to_number(tmp, 0, UINT_MAX, &mydata->timeout);
+ else
+ mydata->timeout = 0;
+
+ ipset_free(saved);
+ return 1;
+}
+
+/*
+ * Print and save
+ */
+
+static void
+iptree_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_iptree_create *header = data;
+ struct ip_set_iptree *map = set->settype->header;
+
+ map->timeout = header->timeout;
+}
+
+static void
+iptree_printheader(struct set *set, unsigned options UNUSED)
+{
+ struct ip_set_iptree *mysetdata = set->settype->header;
+
+ if (mysetdata->timeout)
+ printf(" timeout: %u", mysetdata->timeout);
+ printf("\n");
+}
+
+static void
+iptree_printips_sorted(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ struct ip_set_iptree *mysetdata = 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 += IPSET_VALIGN(sizeof(struct ip_set_req_iptree), dont_align);
+ }
+}
+
+static void
+iptree_saveheader(struct set *set, unsigned options UNUSED)
+{
+ struct ip_set_iptree *mysetdata = 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);
+}
+
+static void
+iptree_saveips(struct set *set, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ struct ip_set_iptree *mysetdata = 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 += IPSET_VALIGN(sizeof(struct ip_set_req_iptree), dont_align);
+ }
+}
+
+static void
+iptree_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 = iptree_create_init,
+ .create_parse = iptree_create_parse,
+ .create_final = iptree_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_iptree),
+ .adt_parser = iptree_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_iptree),
+ .initheader = iptree_initheader,
+ .printheader = iptree_printheader,
+ .printips = iptree_printips_sorted, /* We only have sorted version */
+ .printips_sorted = iptree_printips_sorted,
+ .saveheader = iptree_saveheader,
+ .saveips = iptree_saveips,
+
+ .usage = iptree_usage,
+};
+
+CONSTRUCTOR(iptree)
+{
+ settype_register(&settype_iptree);
+
+}
diff --git a/src/ipset_iptreemap.c b/src/ipset_iptreemap.c
new file mode 100644
index 0000000..22bdcb3
--- /dev/null
+++ b/src/ipset_iptreemap.c
@@ -0,0 +1,208 @@
+/* Copyright 2007 Sven Wegener <sven.wegener@stealer.net>
+ *
+ * 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 <limits.h> /* UINT_MAX */
+#include <stdio.h> /* *printf */
+#include <string.h> /* mem* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_iptreemap.h>
+
+#define OPT_CREATE_GC 0x1
+
+static void
+iptreemap_create_init(void *data)
+{
+ struct ip_set_req_iptreemap_create *mydata = data;
+
+ mydata->gc_interval = 0;
+}
+
+static int
+iptreemap_create_parse(int c, char *argv[] UNUSED, void *data,
+ unsigned int *flags)
+{
+ struct ip_set_req_iptreemap_create *mydata = data;
+
+ switch (c) {
+ case 'g':
+ string_to_number(optarg, 0, UINT_MAX, &mydata->gc_interval);
+
+ *flags |= OPT_CREATE_GC;
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
+static void
+iptreemap_create_final(void *data UNUSED, unsigned int flags UNUSED)
+{
+}
+
+static const struct option create_opts[] = {
+ {.name = "gc", .has_arg = required_argument, .val = 'g'},
+ {NULL},
+};
+
+static ip_set_ip_t
+iptreemap_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_iptreemap *mydata = data;
+ ip_set_ip_t mask;
+
+ char *saved = ipset_strdup(arg);
+ char *ptr, *tmp = saved;
+
+ if (strchr(tmp, '/')) {
+ parse_ipandmask(tmp, &mydata->ip, &mask);
+ mydata->end = mydata->ip | ~mask;
+ } else {
+ if ((ptr = strchr(tmp, ':')) != NULL && ++warn_once == 1)
+ fprintf(stderr, "Warning: please use '-' separator token between IP range.\n"
+ "Next release won't support old separator token.\n");
+ ptr = strsep(&tmp, "-:");
+ parse_ip(ptr, &mydata->ip);
+
+ if (tmp) {
+ parse_ip(tmp, &mydata->end);
+ } else {
+ mydata->end = mydata->ip;
+ }
+ }
+
+ ipset_free(saved);
+
+ return 1;
+}
+
+static void
+iptreemap_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_iptreemap_create *header = data;
+ struct ip_set_iptreemap *map = set->settype->header;
+
+ map->gc_interval = header->gc_interval;
+}
+
+static void
+iptreemap_printheader(struct set *set, unsigned int options UNUSED)
+{
+ struct ip_set_iptreemap *mysetdata = set->settype->header;
+
+ if (mysetdata->gc_interval)
+ printf(" gc: %u", mysetdata->gc_interval);
+
+ printf("\n");
+}
+
+static void
+iptreemap_printips_sorted(struct set *set UNUSED, void *data,
+ u_int32_t len, unsigned int options, char dont_align)
+{
+ struct ip_set_req_iptreemap *req;
+ size_t offset = 0;
+
+ while (len >= offset + sizeof(struct ip_set_req_iptreemap)) {
+ req = data + offset;
+
+ printf("%s", ip_tostring(req->ip, options));
+ if (req->ip != req->end)
+ printf("-%s", ip_tostring(req->end, options));
+ printf("\n");
+
+ offset += IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align);
+ }
+}
+
+static void
+iptreemap_saveheader(struct set *set, unsigned int options UNUSED)
+{
+ struct ip_set_iptreemap *mysetdata = set->settype->header;
+
+ printf("-N %s %s", set->name, set->settype->typename);
+
+ if (mysetdata->gc_interval)
+ printf(" --gc %u", mysetdata->gc_interval);
+
+ printf("\n");
+}
+
+static void
+iptreemap_saveips(struct set *set UNUSED, void *data,
+ u_int32_t len, unsigned int options, char dont_align)
+{
+ struct ip_set_req_iptreemap *req;
+ size_t offset = 0;
+
+ while (len >= offset + sizeof(struct ip_set_req_iptreemap)) {
+ req = data + offset;
+
+ printf("-A %s %s", set->name, ip_tostring(req->ip, options));
+
+ if (req->ip != req->end)
+ printf("-%s", ip_tostring(req->end, options));
+
+ printf("\n");
+
+ offset += IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align);
+ }
+}
+
+static void
+iptreemap_usage(void)
+{
+ printf(
+ "-N set iptreemap --gc interval\n"
+ "-A set IP\n"
+ "-D set IP\n"
+ "-T set IP\n"
+ );
+}
+
+static struct settype settype_iptreemap = {
+ .typename = SETTYPE_NAME,
+ .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+ .create_size = sizeof(struct ip_set_req_iptreemap_create),
+ .create_init = iptreemap_create_init,
+ .create_parse = iptreemap_create_parse,
+ .create_final = iptreemap_create_final,
+ .create_opts = create_opts,
+
+ .adt_size = sizeof(struct ip_set_req_iptreemap),
+ .adt_parser = iptreemap_adt_parser,
+
+ .header_size = sizeof(struct ip_set_iptreemap),
+ .initheader = iptreemap_initheader,
+ .printheader = iptreemap_printheader,
+ .printips = iptreemap_printips_sorted,
+ .printips_sorted = iptreemap_printips_sorted,
+ .saveheader = iptreemap_saveheader,
+ .saveips = iptreemap_saveips,
+
+ .usage = iptreemap_usage,
+};
+
+CONSTRUCTOR(iptreemap)
+{
+ settype_register(&settype_iptreemap);
+}
diff --git a/src/ipset_macipmap.c b/src/ipset_macipmap.c
new file mode 100644
index 0000000..fb97cae
--- /dev/null
+++ b/src/ipset_macipmap.c
@@ -0,0 +1,382 @@
+/* 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> /* *printf */
+#include <stdlib.h> /* mem* */
+#include <string.h> /* str* */
+#include <net/ethernet.h> /* ETH_ALEN */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_macipmap.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. */
+static void
+macipmap_create_init(void *data UNUSED)
+{
+ DP("create INIT");
+ /* Nothing */
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+macipmap_create_parse(int c, char *argv[] UNUSED, void *data, unsigned *flags)
+{
+ struct ip_set_req_macipmap_create *mydata = 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. */
+static void
+macipmap_create_final(void *data, unsigned int flags)
+{
+ struct ip_set_req_macipmap_create *mydata = 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 const struct option create_opts[] = {
+ {.name = "from", .has_arg = required_argument, .val = '1'},
+ {.name = "to", .has_arg = required_argument, .val = '2'},
+ {.name = "network", .has_arg = required_argument, .val = '3'},
+ {.name = "matchunset", .has_arg = no_argument, .val = '4'},
+ {NULL},
+};
+
+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 */
+static ip_set_ip_t
+macipmap_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_macipmap *mydata = data;
+ char *saved = ipset_strdup(arg);
+ char *ptr, *tmp = saved;
+
+ DP("macipmap: %p %p", arg, data);
+
+ ptr = strsep(&tmp, ",");
+ if (!tmp) {
+ tmp = saved;
+ ptr = strsep(&tmp, ":%");
+ if (tmp && ++warn_once == 1)
+ fprintf(stderr, "Warning: please use ',' separator token between ip,mac.\n"
+ "Next release won't support old separator tokens.\n");
+ }
+ 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
+ */
+
+static void
+macipmap_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_macipmap_create *header = data;
+ struct ip_set_macipmap *map = 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;
+}
+
+static void
+macipmap_printheader(struct set *set, unsigned options)
+{
+ struct ip_set_macipmap *mysetdata = 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]);
+}
+
+static inline void
+__macipmap_printips_sorted(struct set *set, void *data,
+ u_int32_t len UNUSED, unsigned options)
+{
+ struct ip_set_macipmap *mysetdata = set->settype->header;
+ struct ip_set_macip *table = data;
+ u_int32_t addr = mysetdata->first_ip;
+
+ while (addr <= mysetdata->last_ip) {
+ if (table[addr - mysetdata->first_ip].match) {
+ printf("%s,", ip_tostring(addr, options));
+ print_mac(table[addr - mysetdata->first_ip].
+ ethernet);
+ printf("\n");
+ }
+ addr++;
+ }
+}
+
+static void
+macipmap_printips_sorted(struct set *set, void *data,
+ u_int32_t len, unsigned options,
+ char dont_align)
+{
+ struct ip_set_req_macipmap *d;
+ size_t offset = 0;
+
+ if (dont_align)
+ return __macipmap_printips_sorted(set, data, len, options);
+
+ while (offset < len) {
+ d = data + offset;
+ printf("%s,", ip_tostring(d->ip, options));
+ print_mac(d->ethernet);
+ printf("\n");
+ offset += IPSET_ALIGN(sizeof(struct ip_set_req_macipmap));
+ }
+}
+
+static void
+macipmap_saveheader(struct set *set, unsigned options)
+{
+ struct ip_set_macipmap *mysetdata = 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");
+}
+
+static inline void
+__macipmap_saveips(struct set *set, void *data,
+ u_int32_t len UNUSED, unsigned options)
+{
+ struct ip_set_macipmap *mysetdata = set->settype->header;
+ struct ip_set_macip *table = data;
+ u_int32_t addr = mysetdata->first_ip;
+
+ while (addr <= mysetdata->last_ip) {
+ if (table[addr - mysetdata->first_ip].match) {
+ printf("-A %s %s,",
+ set->name, ip_tostring(addr, options));
+ print_mac(table[addr - mysetdata->first_ip].
+ ethernet);
+ printf("\n");
+ }
+ addr++;
+ }
+}
+
+static void
+macipmap_saveips(struct set *set, void *data,
+ u_int32_t len, unsigned options,
+ char dont_align)
+{
+ struct ip_set_req_macipmap *d;
+ size_t offset = 0;
+
+ if (dont_align)
+ return __macipmap_saveips(set, data, len, options);
+
+ while (offset < len) {
+ d = data + offset;
+ printf("-A %s %s,", set->name, ip_tostring(d->ip, options));
+ print_mac(d->ethernet);
+ printf("\n");
+ offset += IPSET_ALIGN(sizeof(struct ip_set_req_macipmap));
+ }
+}
+
+static void
+macipmap_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 = macipmap_create_init,
+ .create_parse = macipmap_create_parse,
+ .create_final = macipmap_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_macipmap),
+ .adt_parser = macipmap_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_macipmap),
+ .initheader = macipmap_initheader,
+ .printheader = macipmap_printheader,
+ .printips = macipmap_printips_sorted,
+ .printips_sorted = macipmap_printips_sorted,
+ .saveheader = macipmap_saveheader,
+ .saveips = macipmap_saveips,
+
+ .usage = macipmap_usage,
+};
+
+CONSTRUCTOR(macipmap)
+{
+ settype_register(&settype_macipmap);
+
+}
diff --git a/src/ipset_nethash.c b/src/ipset_nethash.c
new file mode 100644
index 0000000..c7891cf
--- /dev/null
+++ b/src/ipset_nethash.c
@@ -0,0 +1,308 @@
+/* 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 <limits.h> /* UINT_MAX */
+#include <stdio.h> /* *printf */
+#include <string.h> /* mem*, str* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_nethash.h>
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_HASHSIZE 0x01U
+#define OPT_CREATE_PROBES 0x02U
+#define OPT_CREATE_RESIZE 0x04U
+
+/* Initialize the create. */
+static void
+nethash_create_init(void *data)
+{
+ struct ip_set_req_nethash_create *mydata = 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 */
+static int
+nethash_create_parse(int c, char *argv[] UNUSED, void *data, unsigned *flags)
+{
+ struct ip_set_req_nethash_create *mydata = 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. */
+static void
+nethash_create_final(void *data UNUSED, unsigned int flags UNUSED)
+{
+}
+
+/* Create commandline options */
+static const struct option create_opts[] = {
+ {.name = "hashsize", .has_arg = required_argument, .val = '1'},
+ {.name = "probes", .has_arg = required_argument, .val = '2'},
+ {.name = "resize", .has_arg = required_argument, .val = '3'},
+ {NULL},
+};
+
+/* Add, del, test parser */
+static ip_set_ip_t
+nethash_adt_parser(int cmd, const char *arg, void *data)
+{
+ struct ip_set_req_nethash *mydata = data;
+ char *saved = ipset_strdup(arg);
+ 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'", arg);
+ } else
+ if (string_to_number(tmp, 1, 31, &cidr))
+ exit_error(PARAMETER_PROBLEM,
+ "Out of range cidr `%s' specified", arg);
+
+ mydata->cidr = cidr;
+ parse_ip(ptr, &mydata->ip);
+#if 0
+ if (!mydata->ip)
+ exit_error(PARAMETER_PROBLEM,
+ "Zero valued IP address `%s' specified", ptr);
+#endif
+ ipset_free(saved);
+
+ return 1;
+};
+
+/*
+ * Print and save
+ */
+
+static void
+nethash_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_nethash_create *header = data;
+ struct ip_set_nethash *map = set->settype->header;
+
+ memset(map, 0, sizeof(struct ip_set_nethash));
+ map->hashsize = header->hashsize;
+ map->probes = header->probes;
+ map->resize = header->resize;
+}
+
+static void
+nethash_printheader(struct set *set, unsigned options UNUSED)
+{
+ struct ip_set_nethash *mysetdata = 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 UNUSED)
+{
+ 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), 0), buf);
+ return buf;
+}
+
+static void
+nethash_printips(struct set *set UNUSED, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ size_t offset = 0;
+ ip_set_ip_t *ip;
+
+ while (offset < len) {
+ ip = data + offset;
+ printf("%s\n", unpack_ip_tostring(*ip, options));
+ offset += IPSET_VALIGN(sizeof(ip_set_ip_t), dont_align);
+ }
+}
+
+static void
+nethash_saveheader(struct set *set, unsigned options UNUSED)
+{
+ struct ip_set_nethash *mysetdata = 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 */
+static void
+nethash_saveips(struct set *set UNUSED, void *data, u_int32_t len,
+ unsigned options, char dont_align)
+{
+ size_t offset = 0;
+ ip_set_ip_t *ip;
+
+ while (offset < len) {
+ ip = data + offset;
+ printf("-A %s %s\n", set->name,
+ unpack_ip_tostring(*ip, options));
+ offset += IPSET_VALIGN(sizeof(ip_set_ip_t), dont_align);
+ }
+}
+
+static void
+nethash_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 = nethash_create_init,
+ .create_parse = nethash_create_parse,
+ .create_final = nethash_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_nethash),
+ .adt_parser = nethash_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_nethash),
+ .initheader = nethash_initheader,
+ .printheader = nethash_printheader,
+ .printips = nethash_printips,
+ .printips_sorted = nethash_printips,
+ .saveheader = nethash_saveheader,
+ .saveips = nethash_saveips,
+
+ .usage = nethash_usage,
+};
+
+CONSTRUCTOR(nethash)
+{
+ settype_register(&settype_nethash);
+
+}
diff --git a/src/ipset_portmap.c b/src/ipset_portmap.c
new file mode 100644
index 0000000..a1065ae
--- /dev/null
+++ b/src/ipset_portmap.c
@@ -0,0 +1,272 @@
+/* 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> /* *printf */
+#include <string.h> /* mem* */
+
+#include "ipset.h"
+
+#include <linux/netfilter_ipv4/ip_set_portmap.h>
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM 0x01U
+#define OPT_CREATE_TO 0x02U
+
+#define OPT_ADDDEL_PORT 0x01U
+
+/* Initialize the create. */
+static void
+portmap_create_init(void *data UNUSED)
+{
+ DP("create INIT");
+ /* Nothing */
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+portmap_create_parse(int c, char *argv[] UNUSED, void *data, unsigned *flags)
+{
+ struct ip_set_req_portmap_create *mydata = 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. */
+static void
+portmap_create_final(void *data, unsigned int flags)
+{
+ struct ip_set_req_portmap_create *mydata = 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 const struct option create_opts[] = {
+ {.name = "from", .has_arg = required_argument, .val = '1'},
+ {.name = "to", .has_arg = required_argument, .val = '2'},
+ {NULL},
+};
+
+/* Add, del, test parser */
+static ip_set_ip_t
+portmap_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_portmap *mydata = data;
+
+ parse_port(arg, &mydata->ip);
+ DP("%s", port_tostring(mydata->ip, 0));
+
+ return 1;
+}
+
+/*
+ * Print and save
+ */
+
+static void
+portmap_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_portmap_create *header = data;
+ struct ip_set_portmap *map = set->settype->header;
+
+ memset(map, 0, sizeof(struct ip_set_portmap));
+ map->first_ip = header->from;
+ map->last_ip = header->to;
+}
+
+static void
+portmap_printheader(struct set *set, unsigned options)
+{
+ struct ip_set_portmap *mysetdata = set->settype->header;
+
+ printf(" from: %s", port_tostring(mysetdata->first_ip, options));
+ printf(" to: %s\n", port_tostring(mysetdata->last_ip, options));
+}
+
+static inline void
+__portmap_printips_sorted(struct set *set, void *data,
+ u_int32_t len UNUSED, unsigned options)
+{
+ struct ip_set_portmap *mysetdata = set->settype->header;
+ ip_set_ip_t addr = mysetdata->first_ip;
+
+ DP("%u -- %u", mysetdata->first_ip, mysetdata->last_ip);
+ while (addr <= mysetdata->last_ip) {
+ if (test_bit(addr - mysetdata->first_ip, data))
+ printf("%s\n", port_tostring(addr, options));
+ addr++;
+ }
+}
+
+static void
+portmap_printips_sorted(struct set *set, void *data,
+ u_int32_t len, unsigned options,
+ char dont_align)
+{
+ ip_set_ip_t *ip;
+ size_t offset = 0;
+
+ if (dont_align)
+ return __portmap_printips_sorted(set, data, len, options);
+
+ while (offset < len) {
+ ip = data + offset;
+ printf("%s\n", port_tostring(*ip, options));
+ offset += IPSET_ALIGN(sizeof(ip_set_ip_t));
+ }
+}
+
+static void
+portmap_saveheader(struct set *set, unsigned options)
+{
+ struct ip_set_portmap *mysetdata = set->settype->header;
+
+ printf("-N %s %s --from %s",
+ set->name,
+ set->settype->typename,
+ port_tostring(mysetdata->first_ip, options));
+ printf(" --to %s\n",
+ port_tostring(mysetdata->last_ip, options));
+}
+
+static inline void
+__portmap_saveips(struct set *set, void *data,
+ u_int32_t len UNUSED, unsigned options)
+{
+ struct ip_set_portmap *mysetdata = set->settype->header;
+ ip_set_ip_t addr = mysetdata->first_ip;
+
+ while (addr <= mysetdata->last_ip) {
+ DP("addr: %lu, last_ip %lu", (long unsigned)addr, (long unsigned)mysetdata->last_ip);
+ if (test_bit(addr - mysetdata->first_ip, data))
+ printf("-A %s %s\n",
+ set->name,
+ port_tostring(addr, options));
+ addr++;
+ }
+}
+
+static void
+portmap_saveips(struct set *set, void *data,
+ u_int32_t len, unsigned options,
+ char dont_align)
+{
+ ip_set_ip_t *ip;
+ size_t offset = 0;
+
+ if (dont_align)
+ return __portmap_saveips(set, data, len, options);
+
+ while (offset < len) {
+ ip = data + offset;
+ printf("-A %s %s\n", set->name, port_tostring(*ip, options));
+ offset += IPSET_ALIGN(sizeof(ip_set_ip_t));
+ }
+}
+
+static void
+portmap_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 = portmap_create_init,
+ .create_parse = portmap_create_parse,
+ .create_final = portmap_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_portmap),
+ .adt_parser = portmap_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_portmap),
+ .initheader = portmap_initheader,
+ .printheader = portmap_printheader,
+ .printips = portmap_printips_sorted,
+ .printips_sorted = portmap_printips_sorted,
+ .saveheader = portmap_saveheader,
+ .saveips = portmap_saveips,
+
+ .usage = portmap_usage,
+};
+
+CONSTRUCTOR(portmap)
+{
+ settype_register(&settype_portmap);
+
+}
diff --git a/src/ipset_setlist.c b/src/ipset_setlist.c
new file mode 100644
index 0000000..de16c44
--- /dev/null
+++ b/src/ipset_setlist.c
@@ -0,0 +1,229 @@
+/* Copyright 2008 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_setlist.h>
+#include "ipset.h"
+
+/* Initialize the create. */
+static void
+setlist_create_init(void *data)
+{
+ struct ip_set_req_setlist_create *mydata = data;
+
+ mydata->size = 8;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+setlist_create_parse(int c, char *argv[] UNUSED, void *data,
+ unsigned *flags UNUSED)
+{
+ struct ip_set_req_setlist_create *mydata = data;
+ unsigned int size;
+
+ switch (c) {
+ case '1':
+ if (string_to_number(optarg, 1, 255, &size))
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid size '%s specified: must be "
+ "between 1-255", optarg);
+ mydata->size = size;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/* Final check; exit if not ok. */
+static void
+setlist_create_final(void *data UNUSED, unsigned int flags UNUSED)
+{
+}
+
+/* Create commandline options */
+static const struct option create_opts[] = {
+ {.name = "size", .has_arg = required_argument, .val = '1'},
+ {NULL},
+};
+
+static void
+check_setname(const char *name)
+{
+ if (strlen(name) > IP_SET_MAXNAMELEN - 1)
+ exit_error(PARAMETER_PROBLEM,
+ "Setname %s is longer than %d characters.",
+ name, IP_SET_MAXNAMELEN - 1);
+}
+
+/* Add, del, test parser */
+static ip_set_ip_t
+setlist_adt_parser(int cmd UNUSED, const char *arg, void *data)
+{
+ struct ip_set_req_setlist *mydata = data;
+ char *saved = ipset_strdup(arg);
+ char *ptr, *tmp = saved;
+
+ DP("setlist: %p %p", arg, data);
+
+ ptr = strsep(&tmp, ",");
+ check_setname(ptr);
+ strcpy(mydata->name, ptr);
+
+ if (!tmp) {
+ mydata->before = 0;
+ mydata->ref[0] = '\0';
+ return 1;
+ }
+
+ ptr = strsep(&tmp, ",");
+
+ if (tmp == NULL || !(strcmp(ptr, "before") == 0 || strcmp(ptr, "after") == 0))
+ exit_error(PARAMETER_PROBLEM,
+ "Syntax error, you must specify elements as setname,[before|after],setname");
+
+ check_setname(tmp);
+ strcpy(mydata->ref, tmp);
+ mydata->before = !strcmp(ptr, "before");
+
+ free(saved);
+
+ return 1;
+}
+
+/*
+ * Print and save
+ */
+
+static void
+setlist_initheader(struct set *set, const void *data)
+{
+ const struct ip_set_req_setlist_create *header = data;
+ struct ip_set_setlist *map = set->settype->header;
+
+ memset(map, 0, sizeof(struct ip_set_setlist));
+ map->size = header->size;
+}
+
+static void
+setlist_printheader(struct set *set, unsigned options UNUSED)
+{
+ struct ip_set_setlist *mysetdata = set->settype->header;
+
+ printf(" size: %u\n", mysetdata->size);
+}
+
+static void
+setlist_printips_sorted(struct set *set, void *data,
+ u_int32_t len UNUSED, unsigned options UNUSED,
+ char dont_align)
+{
+ struct ip_set_setlist *mysetdata = set->settype->header;
+ int i, asize;
+ ip_set_id_t *id;
+ struct set *elem;
+
+ asize = IPSET_VALIGN(sizeof(ip_set_id_t), dont_align);
+ for (i = 0; i < mysetdata->size; i++ ) {
+ DP("Try %u", i);
+ id = (ip_set_id_t *)(data + i * asize);
+ DP("Try %u, check", i);
+ if (*id == IP_SET_INVALID_ID)
+ return;
+ elem = set_find_byid(*id);
+ printf("%s\n", elem->name);
+ }
+}
+
+static void
+setlist_saveheader(struct set *set, unsigned options UNUSED)
+{
+ struct ip_set_setlist *mysetdata = set->settype->header;
+
+ printf("-N %s %s --size %u\n",
+ set->name, set->settype->typename,
+ mysetdata->size);
+}
+
+static void
+setlist_saveips(struct set *set, void *data,
+ u_int32_t len UNUSED, unsigned options UNUSED, char dont_align)
+{
+ struct ip_set_setlist *mysetdata = set->settype->header;
+ int i, asize;
+ ip_set_id_t *id;
+ struct set *elem;
+
+ asize = IPSET_VALIGN(sizeof(ip_set_id_t), dont_align);
+ for (i = 0; i < mysetdata->size; i++ ) {
+ id = (ip_set_id_t *)(data + i * asize);
+ if (*id == IP_SET_INVALID_ID)
+ return;
+ elem = set_find_byid(*id);
+ printf("-A %s %s\n", set->name, elem->name);
+ }
+}
+
+static void
+setlist_usage(void)
+{
+ printf
+ ("-N set setlist --size size\n"
+ "-A set setname[,before|after,setname]\n"
+ "-D set setname\n"
+ "-T set setname\n");
+}
+
+static struct settype settype_setlist = {
+ .typename = SETTYPE_NAME,
+ .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+ /* Create */
+ .create_size = sizeof(struct ip_set_req_setlist_create),
+ .create_init = setlist_create_init,
+ .create_parse = setlist_create_parse,
+ .create_final = setlist_create_final,
+ .create_opts = create_opts,
+
+ /* Add/del/test */
+ .adt_size = sizeof(struct ip_set_req_setlist),
+ .adt_parser = setlist_adt_parser,
+
+ /* Printing */
+ .header_size = sizeof(struct ip_set_setlist),
+ .initheader = setlist_initheader,
+ .printheader = setlist_printheader,
+ .printips = setlist_printips_sorted,
+ .printips_sorted = setlist_printips_sorted,
+ .saveheader = setlist_saveheader,
+ .saveips = setlist_saveips,
+
+ .usage = setlist_usage,
+};
+
+CONSTRUCTOR(setlist)
+{
+ settype_register(&settype_setlist);
+
+}