From def84eeeae05416e161f884e62f7b195e0079b5c Mon Sep 17 00:00:00 2001 From: "/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kadlec/emailAddress=kadlec@netfilter.org" Date: Wed, 1 Dec 2004 09:07:34 +0000 Subject: ipset 2.0 committed --- ChangeLog | 15 +- Makefile | 60 +- ipset.8 | 302 +++--- ipset.c | 2716 +++++++++++++++++++++++++----------------------------- ipset.h | 115 +-- ipset_iphash.c | 382 ++------ ipset_ipmap.c | 146 +-- ipset_macipmap.c | 155 +--- ipset_portmap.c | 141 +-- libipt_set.h | 103 +-- 10 files changed, 1762 insertions(+), 2373 deletions(-) diff --git a/ChangeLog b/ChangeLog index c6e20fa..4ed34ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,13 @@ +2.0 + - Chaining of sets are changed: child sets replaced by bindings + - Kernel-userspace communication reorganized to minimize the number + of syscalls + - Save and restore functionality implemented + - iphash type reworked: clashing resolved by double-hashing and by + dynamically growing the set 1.0 - - Renamed to ipset (Jozsef Kadlecsik) - - Rewritten to support child pools (Jozsef Kadlecsik) - - portmap, iphash pool support added (Jozsef Kadlecsik) - - too much other mods here and there to list... (Jozsef Kadlecsik) + - Renamed to ipset + - Rewritten to support child pools + - portmap, iphash pool support added + - too much other mods here and there to list... diff --git a/Makefile b/Makefile index 23302b0..bba6930 100644 --- a/Makefile +++ b/Makefile @@ -1,48 +1,64 @@ #!/usr/bin/make -IPSET_VERSION:=v1.0 +###################################################################### +# YOU SHOULD NOT NEED TO TOUCH ANYTHING BELOW THIS LINE +###################################################################### + +ifndef KERNEL_DIR +KERNEL_DIR=/usr/src/linux +endif + +IPSET_VERSION:=2.0 + +PREFIX:=/usr/local +LIBDIR:=$(PREFIX)/lib +BINDIR:=$(PREFIX)/sbin +MANDIR:=$(PREFIX)/man +INCDIR:=$(PREFIX)/include IPSET_LIB_DIR:=$(DESTDIR)$(LIBDIR)/ipset -#IPSET_LIB_DIR:=. -#CFLAGS:=-I$(KERNEL_DIR)/include + +# directory for new iptables releases +RELEASE_DIR:=/tmp + +COPT_FLAGS:=-O2 +CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -I$(KERNEL_DIR)/include -I. -g -DIPSET_DEBUG #-pg # -DIPTC_DEBUG SETTYPES:=ipmap portmap macipmap iphash -EXTRAS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_set.h ] && echo ipset/ipset) -EXTRAS+=$(foreach T, $(SETTYPES),ipset/libipset_$(T).so) -EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ipset $(DESTDIR)$(MANDIR)/man8/ipset.8 -EXTRA_INSTALLS+=$(foreach T, $(SETTYPES), $(DESTDIR)$(LIBDIR)/ipset/libipset_$(T).so) +PROGRAMS=ipset +SHARED_LIBS=$(foreach T, $(SETTYPES),libipset_$(T).so) +INSTALL=$(DESTDIR)$(BINDIR)/ipset $(DESTDIR)$(MANDIR)/man8/ipset.8 +INSTALL+=$(foreach T, $(SETTYPES), $(DESTDIR)$(LIBDIR)/ipset/libipset_$(T).so) -ifndef TOPLEVEL_INCLUDED -local: - cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS) +all: $(PROGRAMS) $(SHARED_LIBS) -else -EXTRA_DEPENDS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_set.h ] && echo "") -CFLAGS+=-DIPSET_VERSION=$(IPSET_VERSION) -DIPSET_LIB_DIR=\"$(IPSET_LIB_DIR)\" +install: all $(INSTALL) + +clean: $(EXTRA_CLEANS) + rm -rf $(PROGRAMS) $(SHARED_LIBS) *.o #The ipset(8) self -ipset/ipset.o: ipset/ipset.c - $(CC) $(CFLAGS) -g -c -o $@ $< +ipset.o: ipset.c + $(CC) $(CFLAGS) -DIPSET_VERSION=\"$(IPSET_VERSION)\" -DIPSET_LIB_DIR=\"$(IPSET_LIB_DIR)\" -c -o $@ $< -ipset/ipset: ipset/ipset.o +ipset: ipset.o $(CC) $(CFLAGS) -ldl -rdynamic -o $@ $^ #Pooltypes -ipset/ipset_%.o: ipset/ipset_%.c +ipset_%.o: ipset_%.c $(CC) $(CFLAGS) -c -o $@ $< -ipset/libipset_%.so: ipset/ipset_%.o +libipset_%.so: ipset_%.o $(LD) -shared -o $@ $< -$(DESTDIR)$(LIBDIR)/ipset/libipset_%.so: ipset/libipset_%.so +$(DESTDIR)$(LIBDIR)/ipset/libipset_%.so: libipset_%.so @[ -d $(DESTDIR)$(LIBDIR)/ipset ] || mkdir -p $(DESTDIR)$(LIBDIR)/ipset cp $< $@ -$(DESTDIR)$(BINDIR)/ipset: ipset/ipset +$(DESTDIR)$(BINDIR)/ipset: ipset @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) cp $< $@ -$(DESTDIR)$(MANDIR)/man8/ipset.8: ipset/ipset.8 +$(DESTDIR)$(MANDIR)/man8/ipset.8: ipset.8 @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8 cp $< $@ -endif diff --git a/ipset.8 b/ipset.8 index 721bc43..f2c2f02 100644 --- a/ipset.8 +++ b/ipset.8 @@ -18,7 +18,7 @@ .\" .\" .SH NAME -ipset \- IP set administration +ipset \- administration tool for IP sets .SH SYNOPSIS .BR "ipset -N " "set type-specification [options]" .br @@ -26,27 +26,30 @@ ipset \- IP set administration .br .BR "ipset -[EW] " "from-set to-set" .br -.BR "ipset -[ADT] " "set entry" +.BR "ipset -[ADU] " "set entry" +.br +.BR "ipset -B " "set entry -b binding" +.br +.BR "ipset -T " "set entry [-b binding]" .br .BR "ipset -R " .SH DESCRIPTION -.B Ipset -is used to set up, maintain, and inspect so called IP sets in the Linux -kernel. Each set can contain a fixed number of entries and at every entry -there might be child sets, up to a given level. Child sets at the same -level must have the same type. +.B ipset +is used to set up, maintain and inspect so called IP sets in the Linux +kernel. Depending on the type, an IP set may store IP addresses, (TCP/UDP) +port numbers or additional informations besides IP addresses: the word IP +means a general term here. See the set type definitions below. .P -In spite it is called IP sets, depending on the type the sets may -store not only IP addresses but (TCP/UDP) port numbers as well, or -additional informations besides IP addresses: the word IP means a general -term here. See the set type definitions below. +Any entry in a set can be bound to another set, which forms a relationship +between a set element and the set it is bound to. The sets may have a +default binding, which is valid for every set element for which there is +no binding defined at all. There is no need for the entry to be +added to the set for a binding to be defined for it. .P -Child sets are identified -by `IP', without the possible additional informations stored in the set. -There is no other relationship between the set entry and the child set -at that entry: the set entry can be added, deleted without disturbing -the child set and vice versa. Therefore below we denote pure IPs -identifying a child set by `IP', while entries in a set by `entry'. +IP set bindings pointing to sets and iptables matches and targets +referring to sets creates references, which protects the given sets in +the kernel. A set cannot be removed (destroyed) while there is a reference +pointing to it. .SH OPTIONS The options that are recognized by .B ipset @@ -59,104 +62,135 @@ need to use only enough letters to ensure that .B ipset can differentiate it from all other options. .TP -.BI "-N, --create " "\fIsetname\fP type0[,type1...] type0-options" -Create a set identified with setname, with listed types. -Type0-specific options must be supplied. When more than one set -type is given on the command line, room for the first level child -sets are automatically reserved. -.TP -.BI "-N, --create " "\fIsetname:IP[,...]\fP type type-options" -Create a child set at setname:IP[,...] identified with type, -which must correspond to the set type used when the set was created. -The parent set and parent child sets must exist. When the -.B "-c, --childsets" -option is used, space for child sets will be reserved. -.TP -.BI "-X, --destroy " "[\fIsetname[:IP,...]\fP]" -Destroy the specified set or child set, or all sets if none is -given. -.TP -.BI "-F, --flush " "[\fIsetname[:IP,...]\fP]" -Delete all entries from the specified set or child set, or from -all sets if none is given. When the -.B "-c, --childsets" -option is specified, child sets are recursively flushed too. +.BI "-N, --create " "\fIsetname\fP type type-options" +Create a set identified with setname and specified type. +Type-specific options must be supplied. +.TP +.BI "-X, --destroy " "[\fIsetname\fP]" +Destroy the specified set, or all sets if none or the keyword +.B +:all: +is specified. +Before destroying the set, all bindings belonging to the +set elements and the default binding of the set are removed. + +If the set has got references, nothing is done. +.TP +.BI "-F, --flush " "[\fIsetname\fP]" +Delete all entries from the specified set, or flush +all sets if none or the keyword +.B +:all: +is given. Bindings are not affected by the flush operation. .TP .BI "-E, --rename " "\fIfrom-setname\fP \fIto-setname\fP" Rename a set. Set identified by to-setname must not exist. .TP .BI "-W, --swap " "\fIfrom-setname\fP \fIto-setname\fP" -Swap the content of two sets. Both sets must exist. -.TP -.BI "-L, --list " "[\fIsetname[:IP,...]\fP]" -List the entries from the specified set or child set, or from -all sets if none is given. When the -.B "-c, --childsets" -option is specified, child sets are recursively listed too. -The +Swap two sets as they referenced in the Linux kernel. +.B +iptables +rules or +.B +ipset +bindings pointing to from-setname will point to to-setname +and vice versa. Both sets must exist. +.TP +.BI "-L, --list " "[\fIsetname\fP]" +List the entries and bindings for the specified set, or for +all sets if none or the keyword +.B +:all: +is given. The .B "-n, --numeric" option can be used to suppress name lookups and generate numeric output. When the .B "-s, --sorted" -option is given, the entries are listed sorted. +option is given, the entries are listed sorted (if the given set +supports it). .TP -.BI "-S, --save " "[\fIsetname[:IP,...]\fP]" -Save the set or child set, or all sets if none specified to stdout -in a format, that --restore can read. +.BI "-S, --save " "[\fIsetname\fP]" +Save the given set, or all sets if none or the keyword +.B +:all: +is specified to stdout in a format that --restore can read. .TP .BI "-R, --restore " -Restore from stdin a saved session generated by --save. -.TP -.BI "-A, --add " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP" -Add an entry to a (child) set or multiple entries to the child sets -defined by the entries. In another words, the command -.nf - ipset -A set entry1,entry2 -.fi -is equivalent to -.nf - ipset -A set entry1 - ipset -A set:entry1-IP entry2 -.fi -.TP -.BI "-D, --del " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP" -Delete an entry from a (child) set or multiple entries from the child sets -defined by the entries. +Restore a saved session generated by --save. The saved session +is read from stdin which is required to be rewindable. +.TP +.BI "-A, --add " "\fIsetname\fP \fIIP\fP" +Add an IP to a set. +.TP +.BI "-D, --del " "\fIsetname\fP \fIIP\fP" +Delete an IP from a set. +.TP +.BI "-T, --test " "\fIsetname\fP \fIIP +Test wether an IP is in a set or not. Exit status number is zero +if the tested IP is in the set and nonzero if it is missing from +the set. +.TP +.BI "-T, --test " "\fIsetname\fP \fIIP\fP \fI--binding\fP \fIto-setname\fP" +Test wether the IP belonging to the set points to the specified binding. +Exit status number is zero if the binding points to the specified set, +otherwise it is nonzero. The keyword +.B +:default: +can be used to test the default binding of the set. +.TP +.BI "-B, --bind " "\fIsetname\fP \fIIP\fP \fI--binding\fP \fIto-setname\fP" +Bind the IP in setname to to-setname. +.TP +.BI "-U, --unbind " "\fIsetname\fP \fIIP\fP" +Delete the binding belonging to IP in set setname. +.TP +.BI "-H, --help " "[settype]" +Print help and settype specific help if settype specified. .P -When multiple entries are added or deleted and the command fails at -a deeper level, the successfully added/deleted entries are not restored -to their previous value. -.TP -.BI "-T, --test " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP" -Test if an entry or multiple entries exist in a (child) set. Exit status -number is nonzero if any tested entry is missing from the set and -zero if they are all exists. -.TP -.BI "-H, --help " "[settype] [options]" -Print help and settype specific help. Some set types has additional -help options, see below. +At the +.B +-B, -U +and +.B +-T +commands you can use the token +.B +:default: +to bind, unbind or test the default binding of a set instead +of an IP. At the +.B +-U +command you can use the token +.B +:all: +to destroy the bindings of all elements of a set. .SS "OTHER OPTIONS" The following additional options can be specified: .TP +.B "-b, --binding setname" +The option specifies the value of the binding for the +.B "-B" +binding command, for which it is a mandatory option. +You can use it in the +.B "-T" +test command as well to test bindings. +.TP .B "-s, --sorted" Sorted output. When listing sets, entries are listed sorted. .TP .B "-n, --numeric" -Numeric output. When listing sets, IP addresses and port numbers -will be printed in numeric format. By default the program will -try to display them as host names, network names, or services -(whenever applicable). +Numeric output. When listing sets, bindings, IP addresses and +port numbers will be printed in numeric format. By default the +program will try to display them as host names, network names +or services (whenever applicable), which can trigger +.B +slow +DNS +lookups. .TP .B "-q, --quiet" -Suppress any output to stdout and stderr. Ipset will still return +Suppress any output to stdout and stderr. ipset will still return possible errors. -.TP -.B "-c, --childsets" -Space will be reserved for child sets when creating a set, or list/flush -operation valid for child sets too. -.TP -.B "-i, --hint" -Hint best settype initialization parameters (for hash type sets). .SH SET TYPES ipset supports the following set types: .SS ipmap @@ -191,7 +225,7 @@ The macipmap set type uses a memory range, where each 8 bytes represents one IP and a MAC addresses. A macipmap set type can store up to 65535 (B-class network) IP addresses with MAC. When adding an entry to a macipmap set, you must specify the entry as -.I IP%MAC +.I IP%MAC. When deleting or testing macipmap entries, the .I %MAC part is not mandatory. @@ -223,86 +257,40 @@ Options to use when creating an portmap set: .BR "--to " to-port Create a portmap set from the specified range. .SS iphash -The iphash set type uses a fixed size hash to store the IP addresses -and can store up to 65535 (size of a B-class network) addresses. The -iphash set type is fast and great for use to store -random addresses. By supplyig the +The iphash set type uses a hash to store IP addresses. +In order to avoid clashes in the hash, double-hashing and, as a last +resort, dynamic growing of the hash performed. The iphash set type is +fast and great for use to store random addresses. By supplyig the .B "--netmask" option with a CIDR netmask value between 0-32 at creating the set, you will be able to store and match network addresses instead: i.e an IP address will be in the set if the value of the address masked with the specified netmask can be found in the set. -When adding and IP address to the hash, the IP address can be preceded -by `+', by which you can force to overwrite already existing entries -in the hash. -.P -In help mode you can use the -.B "--hint" -option to find the the smallest hashsize with the corresponding initval -for your hash entries. .P Options to use when creating an iphash set: .TP .BR "--hashsize " hashsize +The initial hash size (default 1024) .TP -.BR "--initval " hash-initval -Create an iphash set with the specified hashsize and initial -(random) hash parameter. (The -.B "--initval" -parameter is optional.) +.BR "--probes " probes +How many times try to resolve clashing at adding an IP to the hash +by double-hashing (default 8). +.TP +.BR "--resize " percent +Increase the hash size by this many percent (default 50) when adding +an IP to the hash could not be performed after +.B +probes +number of double-hashing. .TP .BR "--netmask " CIDR-netmask When the optional .B "--netmask" parameter specified, network addresses will be stored in the set instead of IP addresses. -.TP -The possible help mode options are: -.TP -.BI "-i, --hint" -Enable hinting mode to find the best (smallest) hash size. The entries -are fed via standard input. -.TP -.BI "--try " number -How many times should the program try the same hash size with -different random initvals (default 8). -.TP -.BI "--factor " number -The starting hash size is so many times the number of the entries -(default 4). -.SH NETFILTER MATCH AND TARGET -The IP set package adds the `set' match and `SET' target to netfilter: -.SS set -The match provides the following option: -.TP -.BR "--set " "setname[:flag,...] flag[,flag]" -where flags are -.BR "src" -and/or -.BR "dst" . -Hence the command -.nf - iptables -A FORWARD -m set --set test:src dst -.fi -will match packets, for which there is a child-set under the set named -as test at the source address or port (depending on the type of the set) -of the packet, and the destination address or port of the -packet (depending of the type of the child set) is set in the child set. -.SS SET -The target provides the following option: -.TP -.BR "--add-set " "setname[:flag,...] flag[,flag]" -add the address(es)/port(s) of the packet to the (child)set -.TP -.BR "--del-set " "setname[:flag,...] flag[,flag]" -delete the address(es)/port(s) of the packet from the (child)set, -where flags are -.BR "src" -and/or -.BR "dst" . -The flags in the second argument can be preceded by an optional `+' sign, -which will force overwriting already existing elements in the target set -when adding elements to a hash type set. +.SH GENERAL RESTRICTIONS +Setnames starting with colon (:) cannot be defined. Zero valued set +entries cannot be used. .SH DIAGNOSTICS Various error messages are printed to standard error. The exit code is 0 for correct functioning. Errors which appear to be caused by diff --git a/ipset.c b/ipset.c index 04d2487..6f903da 100644 --- a/ipset.c +++ b/ipset.c @@ -1,25 +1,16 @@ -/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) +/* Copyright 2000-2002 Joakim Axelsson (gozem@linux.nu) * Patrick Schaaf (bof@bof.de) - * Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * Copyright 2003-2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. */ #include #include #include +#include #include #include #include @@ -31,68 +22,50 @@ #include "ipset.h" -/* The list of all sets */ -static struct set *all_sets = NULL; +char program_name[] = "ipset"; +char program_version[] = IPSET_VERSION; /* The list of loaded set types */ static struct settype *all_settypes = NULL; +/* Array of sets */ +struct set **set_list = NULL; +ip_set_id_t max_sets = 0; + /* Suppress output to stdout and stderr? */ static int option_quiet = 0; -/* Data from the command line: */ -static char *set_name = NULL; /* Name of the set */ -static char *set_typename[IP_SET_LEVELS]; /* Set typenames */ -static unsigned int set_typename_level = 0; /* Size of set_typename */ -static ip_set_ip_t set_ip[IP_SET_LEVELS]; /* IP addresses of child set */ -static unsigned int set_level = 0; /* Size of set_ip */ -static unsigned int ip_level = 0; /* Size of add/del/test addresses */ - -/* Max size of a set when hinting. */ -#define MAX_HINT_SIZE 65536 +/* Data for restore mode */ +static int restore = 0; +void *restore_data = NULL; +struct ip_set_restore *restore_set = NULL; +size_t restore_offset = 0, restore_size; +unsigned line = 0; -#ifdef IP_SET_DEBUG +#ifdef IPSET_DEBUG int option_debug = 0; #endif #define OPTION_OFFSET 256 static unsigned int global_option_offset = 0; -/* Now most of these command parsing functions are borrowed from iptables.c */ - -/* Commands */ -#define CMD_NONE 0x0000U -#define CMD_CREATE 0x0001U /* -N */ -#define CMD_DESTROY 0x0002U /* -X */ -#define CMD_FLUSH 0x0004U /* -F */ -#define CMD_RENAME 0x0008U /* -E */ -#define CMD_SWAP 0x0010U /* -W */ -#define CMD_LIST 0x0020U /* -L */ -#define CMD_SAVE 0x0040U /* -S */ -#define CMD_RESTORE 0x0080U /* -R */ -#define CMD_ADD 0x0100U /* -A */ -#define CMD_DEL 0x0200U /* -D */ -#define CMD_TEST 0x0400U /* -T */ -#define CMD_HELP 0x0800U /* -H */ -#define CMD_VERSION 0x1000U /* -V */ -#define CMD_CREATE_CHILD 0x2000U /* -N !!! */ -#define NUMBER_OF_CMD 13 -static const char cmdflags[] = { +/* Most of these command parsing functions are borrowed from iptables.c */ + +static const char cmdflags[] = { ' ', /* CMD_NONE */ 'N', 'X', 'F', 'E', 'W', 'L', 'S', 'R', - 'A', 'D', 'T', 'H', 'V', + 'A', 'D', 'T', 'B', 'U', 'H', 'V', }; /* Options */ -#define OPT_NONE 0x0000U -#define OPT_NUMERIC 0x0001U /* -n */ -#define OPT_SORTED 0x0002U /* -s */ -#define OPT_QUIET 0x0004U /* -q */ -#define OPT_DEBUG 0x0008U /* -z */ -#define OPT_CHILDSETS 0x0010U /* -c */ -#define OPT_HINT 0x0020U /* -i */ -#define NUMBER_OF_OPT 6 +#define OPT_NONE 0x0000U +#define OPT_NUMERIC 0x0001U /* -n */ +#define OPT_SORTED 0x0002U /* -s */ +#define OPT_QUIET 0x0004U /* -q */ +#define OPT_DEBUG 0x0008U /* -z */ +#define OPT_BINDING 0x0010U /* -b */ +#define NUMBER_OF_OPT 5 static const char optflags[] = - { 'n', 's', 'q', 'z', 'c', 'i' }; + { 'n', 's', 'q', 'z', 'b' }; static struct option opts_long[] = { /* set operations */ @@ -111,14 +84,17 @@ static struct option opts_long[] = { {"del", 1, 0, 'D'}, {"test", 1, 0, 'T'}, + /* binding operations */ + {"bind", 1, 0, 'B'}, + {"unbind", 1, 0, 'U'}, + /* free options */ {"numeric", 0, 0, 'n'}, {"sorted", 0, 0, 's'}, {"quiet", 0, 0, 'q'}, - {"childsets",0, 0, 'c'}, - {"hint", 0, 0, 'i'}, + {"binding", 1, 0, 'b'}, -#ifdef IP_SET_DEBUG +#ifdef IPSET_DEBUG /* debug (if compiled with it) */ {"debug", 0, 0, 'z'}, #endif @@ -132,11 +108,10 @@ static struct option opts_long[] = { }; static char opts_short[] = - "-N:X::F::E:W:L::S::RA:D:T:nsqzciVh::H::"; + "-N:X::F::E:W:L::S::RA:D:T:B:U:nsqzb:Vh::H::"; -/* Table of legal combinations of commands and options. If any of the - * given commands make an option legal, that option is legal (applies to - * CMD_LIST and CMD_ZERO only). +/* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal. * Key: * + compulsory * x illegal @@ -144,22 +119,27 @@ static char opts_short[] = */ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = { - /* -n -s -q -z -c -i*/ - /*CREATE*/ {'x', 'x', ' ', ' ', ' ', 'x'}, - /*DESTROY*/ {'x', 'x', ' ', ' ', 'x', 'x'}, - /*FLUSH*/ {'x', 'x', ' ', ' ', ' ', 'x'}, - /*RENAME*/ {'x', 'x', ' ', ' ', 'x', 'x'}, - /*SWAP*/ {'x', 'x', ' ', ' ', 'x', 'x'}, - /*LIST*/ {' ', ' ', 'x', ' ', ' ', 'x'}, - /*SAVE*/ {'x', 'x', ' ', ' ', 'x', 'x'}, - /*RESTORE*/ {'x', 'x', ' ', ' ', 'x', 'x'}, - /*ADD*/ {'x', 'x', ' ', ' ', 'x', 'x'}, - /*DEL*/ {'x', 'x', ' ', ' ', 'x', 'x'}, - /*TEST*/ {'x', 'x', ' ', ' ', 'x', 'x'}, - /*HELP*/ {'x', 'x', 'x', ' ', 'x', ' '}, - /*VERSION*/ {'x', 'x', 'x', ' ', 'x', 'x'}, + /* -n -s -q -z -b */ + /*CREATE*/ {'x', 'x', ' ', ' ', 'x'}, + /*DESTROY*/ {'x', 'x', ' ', ' ', 'x'}, + /*FLUSH*/ {'x', 'x', ' ', ' ', 'x'}, + /*RENAME*/ {'x', 'x', ' ', ' ', 'x'}, + /*SWAP*/ {'x', 'x', ' ', ' ', 'x'}, + /*LIST*/ {' ', ' ', 'x', ' ', 'x'}, + /*SAVE*/ {'x', 'x', ' ', ' ', 'x'}, + /*RESTORE*/ {'x', 'x', ' ', ' ', 'x'}, + /*ADD*/ {'x', 'x', ' ', ' ', 'x'}, + /*DEL*/ {'x', 'x', ' ', ' ', 'x'}, + /*TEST*/ {'x', 'x', ' ', ' ', ' '}, + /*BIND*/ {'x', 'x', ' ', ' ', '+'}, + /*UNBIND*/ {'x', 'x', ' ', ' ', 'x'}, + /*HELP*/ {'x', 'x', 'x', ' ', 'x'}, + /*VERSION*/ {'x', 'x', 'x', ' ', 'x'}, }; +/* Main parser function */ +int parse_commandline(int argc, char *argv[]); + void exit_tryhelp(int status) { fprintf(stderr, @@ -178,6 +158,8 @@ void exit_error(enum exittype status, char *msg, ...) vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, "\n"); + if (line) + fprintf(stderr, "Restore failed at line %u:\n", line); if (status == PARAMETER_PROBLEM) exit_tryhelp(status); if (status == VERSION_PROBLEM) @@ -212,18 +194,18 @@ static void generic_opt_check(int command, int options) for (i = 0; i < NUMBER_OF_OPT; i++) { legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ - for (j = 0; j < NUMBER_OF_CMD; j++) { - if (!(command & (1 << j))) + for (j = 1; j <= NUMBER_OF_CMD; j++) { + if (command != j) continue; if (!(options & (1 << i))) { - if (commands_v_options[j][i] == '+') + if (commands_v_options[j-1][i] == '+') exit_error(PARAMETER_PROBLEM, "You need to supply the `-%c' " "option for this command\n", optflags[i]); } else { - if (commands_v_options[j][i] != 'x') + if (commands_v_options[j-1][i] != 'x') legal = 1; else if (legal == 0) legal = -1; @@ -246,17 +228,164 @@ static char opt2char(int option) static char cmd2char(int option) { - const char *ptr; - for (ptr = cmdflags; option > 1; option >>= 1, ptr++); + if (option <= CMD_NONE || option > NUMBER_OF_CMD) + return ' '; - return *ptr; + return cmdflags[option]; +} + +static int kernel_getsocket(void) +{ + int sockfd = -1; + + sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (sockfd < 0) + exit_error(OTHER_PROBLEM, + "You need to be root to perform this command."); + + return sockfd; +} + +static void kernel_error(unsigned cmd, int err) +{ + unsigned int i; + struct translate_error { + int err; + unsigned cmd; + char *message; + } table[] = + { /* Generic error codes */ + { EPERM, 0, "Missing capability" }, + { EBADF, 0, "Invalid socket option" }, + { EINVAL, 0, "Size mismatch for expected socket data" }, + { ENOMEM, 0, "Not enough memory" }, + { EFAULT, 0, "Failed to copy data" }, + { EPROTO, 0, "ipset kernel/userspace version mismatch" }, + { EBADMSG, 0, "Unknown command" }, + /* Per command error codes */ + /* Reserved ones for add/del/test to handle internally: + * EEXIST + */ + { ENOENT, CMD_CREATE, "Unknown set type" }, + { ENOENT, 0, "Unknown set" }, + { EAGAIN, 0, "Sets are busy, try again later" }, + { ERANGE, CMD_CREATE, "No free slot remained to add a new set" }, + { ERANGE, 0, "IP/port is outside of the set" }, + { ENOEXEC, CMD_CREATE, "Invalid parameters to create a set" }, + { ENOEXEC, CMD_SWAP, "Sets with different types cannot be swapped" }, + { EEXIST, CMD_CREATE, "Set already exists" }, + { EEXIST, CMD_RENAME, "Set with new name already exists" }, + { EBUSY, 0, "Set is in use, operation not permitted" }, + }; + for (i = 0; i < sizeof(table)/sizeof(struct translate_error); i++) { + if ((table[i].cmd == cmd || table[i].cmd == 0) + && table[i].err == err) + exit_error(err == EPROTO ? VERSION_PROBLEM + : OTHER_PROBLEM, + table[i].message); + } + exit_error(OTHER_PROBLEM, "Error from kernel: %s", strerror(err)); +} + +static void kernel_getfrom(unsigned cmd, void *data, size_t * size) +{ + int res; + int sockfd = kernel_getsocket(); + + /* Send! */ + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + + DP("res=%d errno=%d", res, errno); + + if (res != 0) + kernel_error(cmd, errno); +} + +static int kernel_sendto_handleerrno(unsigned cmd, unsigned op, + void *data, size_t size) +{ + int res; + int sockfd = kernel_getsocket(); + + /* Send! */ + res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + + DP("res=%d errno=%d", res, errno); + + if (res != 0) { + if (errno == EEXIST) + return -1; + else + kernel_error(cmd, errno); + } + + return 0; /* all ok */ +} + +static void kernel_sendto(unsigned cmd, void *data, size_t size) +{ + int res; + int sockfd = kernel_getsocket(); + + /* Send! */ + res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + + DP("res=%d errno=%d", res, errno); + + if (res != 0) + kernel_error(cmd, errno); +} + +static int kernel_getfrom_handleerrno(unsigned cmd, void *data, size_t * size) +{ + int res; + int sockfd = kernel_getsocket(); + + /* Send! */ + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + + DP("res=%d errno=%d", res, errno); + + if (res != 0) { + if (errno == EAGAIN) + return -1; + else + kernel_error(cmd, errno); + } + + return 0; /* all ok */ +} + +static void check_protocolversion(void) +{ + struct ip_set_req_version req_version; + size_t size = sizeof(struct ip_set_req_version); + int sockfd = kernel_getsocket(); + int res; + + req_version.op = IP_SET_OP_VERSION; + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size); + + if (res != 0) { + ipset_printf("I'm of protocol version %u.\n" + "Kernel module is not loaded in, " + "cannot verify kernel version.", + IP_SET_PROTOCOL_VERSION); + return; + } + if (req_version.version != IP_SET_PROTOCOL_VERSION) + exit_error(OTHER_PROBLEM, + "Kernel ipset code is of protocol version %u." + "I'm of protocol version %u.\n" + "Please upgrade your kernel and/or ipset(8) utillity.", + req_version.version, IP_SET_PROTOCOL_VERSION); } static void set_command(int *cmd, const int newcmd) { if (*cmd != CMD_NONE) exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", - cmd2char(newcmd), cmd2char(newcmd)); + cmd2char(*cmd), cmd2char(newcmd)); *cmd = newcmd; } @@ -277,7 +406,7 @@ void *ipset_malloc(size_t size) return NULL; if ((p = malloc(size)) == NULL) { - perror("ipset: malloc failed"); + perror("ipset: not enough memory"); exit(1); } return p; @@ -320,8 +449,6 @@ static char *ip_tohost(const struct in_addr *addr) { struct hostent *host; - DP("ip_tohost()"); - if ((host = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET)) != NULL) { @@ -336,11 +463,11 @@ static char *ip_tonetwork(const struct in_addr *addr) { struct netent *net; - DP("ip_tonetwork()"); - if ((net = getnetbyaddr((long) ntohl(addr->s_addr), - AF_INET)) != NULL) + AF_INET)) != NULL) { + DP("%s", net->n_name); return (char *) net->n_name; + } return (char *) NULL; } @@ -370,17 +497,13 @@ void parse_ip(const char *str, ip_set_ip_t * ip) struct hostent *host; struct in_addr addr; + DP("%s", str); + if (inet_aton(str, &addr) != 0) { *ip = ntohl(addr.s_addr); /* We want host byte order */ - -#ifdef IP_SET_DEBUG - { - /*DEBUG*/ char *p = (char *) ip; - DP("PARSE_IP %x %x %x %x %x %x", *ip, 0xC10BF8A6, - p[0], p[1], p[2], p[3]); - } -#endif - + if (!*ip) + exit_error(PARAMETER_PROBLEM, + "Zero valued IP address `%s' specified", str); return; } @@ -397,15 +520,10 @@ void parse_ip(const char *str, ip_set_ip_t * ip) "Please specify one.", str); *ip = ntohl(((struct in_addr *) host->h_addr_list[0])->s_addr); - -#ifdef IP_SET_DEBUG - { - /*DEBUG*/ char *p = (char *) ip; - DP("PARSE_IP %x %x %x %x %x %x", *ip, 0xC10BF8A6, - p[0], p[1], p[2], p[3]); - } -#endif - + if (!*ip) + exit_error(PARAMETER_PROBLEM, + "Zero valued IP address `%s' specified", + str); return; } @@ -418,7 +536,7 @@ void parse_mask(const char *str, ip_set_ip_t * mask) struct in_addr addr; unsigned int bits; - DP("parse_mask %s", str); + DP("%s", str); if (str == NULL) { /* no mask at all defaults to 32 bits */ @@ -460,13 +578,13 @@ parse_ipandmask(const char *str, ip_set_ip_t * ip, ip_set_ip_t * mask) else parse_ip(buf, ip); - DP("parse_ipandmask: %s ip: %08X (%s) mask: %08X", + DP("%s ip: %08X (%s) mask: %08X", str, *ip, ip_tostring(*ip, 0), *mask); /* Apply the netmask */ *ip &= *mask; - DP("parse_ipandmask: %s ip: %08X (%s) mask: %08X", + DP("%s ip: %08X (%s) mask: %08X", str, *ip, ip_tostring(*ip, 0), *mask); } @@ -479,10 +597,10 @@ char *port_tostring(ip_set_ip_t port, unsigned options) struct servent *service; static char name[] = "65535"; - if (!(options & OPT_NUMERIC) - && (service = getservbyport(htons(port), "tcp"))) - return service->s_name; - + if (!(options & OPT_NUMERIC)) { + if ((service = getservbyport(htons(port), "tcp"))) + return service->s_name; + } sprintf(name, "%u", port); return name; } @@ -524,15 +642,23 @@ string_to_port(const char *str, ip_set_ip_t *port) void parse_port(const char *str, ip_set_ip_t *port) { if ((string_to_number(str, 0, 65535, port) != 0) - && (string_to_port(str, port) != 0)) - exit_error(PARAMETER_PROBLEM, "Invalid TCP port `%s' specified", str); + && (string_to_port(str, port) != 0)) + exit_error(PARAMETER_PROBLEM, + "Invalid TCP port `%s' specified", str); + + if (!*port) + exit_error(PARAMETER_PROBLEM, + "Zero valued port `%s' specified", str); } +/* + * Settype functions + */ static struct settype *settype_find(const char *typename) { struct settype *runner = all_settypes; - DP("settype %s", typename); + DP("%s", typename); while (runner != NULL) { if (strncmp(runner->typename, typename, @@ -576,1051 +702,1012 @@ static struct settype *settype_load(const char *typename) return NULL; /* Never executed, but keep compilers happy */ } +static char *check_set_name(char *setname) +{ + if (strlen(setname) > IP_SET_MAXNAMELEN - 1) + exit_error(PARAMETER_PROBLEM, + "Setname '%s' too long, max %d characters.", + setname, IP_SET_MAXNAMELEN - 1); + + return setname; +} + +static struct settype *check_set_typename(const char *typename) +{ + if (strlen(typename) > IP_SET_MAXNAMELEN - 1) + exit_error(PARAMETER_PROBLEM, + "Typename '%s' too long, max %d characters.", + typename, IP_SET_MAXNAMELEN - 1); + + return settype_load(typename); +} + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + /* Register a new set type */ void settype_register(struct settype *settype) { struct settype *chk; + size_t size; - DP("register_settype '%s'\n", settype->typename); + DP("%s", settype->typename); /* Check if this typename already exists */ chk = settype_find(settype->typename); if (chk != NULL) exit_error(OTHER_PROBLEM, - "Set type '%s' already registred!\n", + "Set type '%s' already registered!\n", settype->typename); /* Check version */ if (settype->protocol_version != IP_SET_PROTOCOL_VERSION) exit_error(OTHER_PROBLEM, - "Set type is of wrong protocol version %u!" - " I'm am of version %u.\n", settype->typename, + "Set type %s is of wrong protocol version %u!" + " I'm of version %u.\n", settype->typename, settype->protocol_version, IP_SET_PROTOCOL_VERSION); + /* Initialize internal data */ + settype->header = ipset_malloc(settype->header_size); + size = MAX(settype->create_size, settype->adt_size); + settype->data = ipset_malloc(size); + /* Insert first */ settype->next = all_settypes; - settype->data = ipset_malloc(settype->create_size); all_settypes = settype; - DP("ip_set: register settype end '%s'\n", settype->typename); + DP("%s registered", settype->typename); } -static int kernel_getsocket(void) +/* Find set functions */ +static struct set *set_find_byid(ip_set_id_t id) { - int sockfd = -1; - - sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); - if (sockfd < 0) - exit_error(OTHER_PROBLEM, - "You need to be root to perform this command."); - - return sockfd; + struct set *set = NULL; + ip_set_id_t i; + + for (i = 0; i < max_sets; i++) + if (set_list[i] && set_list[i]->id == id) { + set = set_list[i]; + break; + } + + if (set == NULL) + exit_error(PARAMETER_PROBLEM, + "Set identified by id %u is not found", id); + return set; } -static void kernel_error(int err) +static struct set *set_find_byname(const char *name) { - /* translate errnos, as returned by our *sockopt() functions */ - exit_error(OTHER_PROBLEM, "Error from kernel: %s", strerror(err)); + struct set *set = NULL; + ip_set_id_t i; + + for (i = 0; i < max_sets; i++) + if (set_list[i] + && strncmp(set_list[i]->name, name, + IP_SET_MAXNAMELEN) == 0) { + set = set_list[i]; + break; + } + if (set == NULL) + exit_error(PARAMETER_PROBLEM, + "Set %s is not found", name); + return set; } -static void kernel_sendto(void *data, size_t size) +static ip_set_id_t set_find_free_index(const char *name) { - int res; - int sockfd = kernel_getsocket(); - - /* Send! */ - res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); - - DP("kernel_sendto() res=%d errno=%d\n", res, errno); + ip_set_id_t i, index = IP_SET_INVALID_ID; + + for (i = 0; i < max_sets; i++) { + if (index == IP_SET_INVALID_ID + && set_list[i] == NULL) + index = i; + if (set_list[i] != NULL + && strncmp(set_list[i]->name, name, + IP_SET_MAXNAMELEN) == 0) + exit_error(PARAMETER_PROBLEM, + "Set %s is already defined, cannot be restored", + name); + } + + if (index == IP_SET_INVALID_ID) + exit_error(PARAMETER_PROBLEM, + "Set %s cannot be restored, " + "max number of set %u reached", + name, max_sets); - if (res != 0) - kernel_error(errno); + return index; } -/* Used by addip and delip that has to handle the EEXIST error better */ -static int kernel_sendto_handleexist(void *data, size_t size) +/* + * Send create set order to kernel + */ +static void set_create(const char *name, struct settype *settype) { - int res; - int sockfd = kernel_getsocket(); + struct ip_set_req_create req_create; + size_t size; + void *data; - /* Send! */ - res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + DP("%s %s", name, settype->typename); + + req_create.op = IP_SET_OP_CREATE; + req_create.version = IP_SET_PROTOCOL_VERSION; + strcpy(req_create.name, name); + strcpy(req_create.typename, settype->typename); + + /* Final checks */ + settype->create_final(settype->data, settype->flags); - DP("kernel_sendto_handleexist() res=%d errno=%d\n", res, errno); + /* Alloc memory for the data to send */ + size = sizeof(struct ip_set_req_create) + settype->create_size; + data = ipset_malloc(size); - if (res != 0 && errno == EEXIST) - return -1; - else if (res != 0) - kernel_error(errno); + /* Add up ip_set_req_create and the settype data */ + memcpy(data, &req_create, sizeof(struct ip_set_req_create)); + memcpy(data + sizeof(struct ip_set_req_create), + settype->data, settype->create_size); - return 0; /* all ok */ + kernel_sendto(CMD_CREATE, data, size); + free(data); } -static void kernel_getfrom(void *data, size_t * size) +static void set_restore_create(const char *name, struct settype *settype) { - int res; - int sockfd = kernel_getsocket(); - - /* Send! */ - res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + struct set *set; + + DP("%s %s %u %u %u %u", name, settype->typename, + restore_offset, sizeof(struct ip_set_restore), + settype->create_size, restore_size); + + /* Sanity checking */ + if (restore_offset + + sizeof(struct ip_set_restore) + + settype->create_size > restore_size) + exit_error(PARAMETER_PROBLEM, + "Giving up, restore file is screwed up!"); + + /* Final checks */ + settype->create_final(settype->data, settype->flags); - DP("kernel_getfrom() res=%d errno=%d\n", res, errno); + /* Fill out restore_data */ + restore_set = (struct ip_set_restore *) + (restore_data + restore_offset); + strcpy(restore_set->name, name); + strcpy(restore_set->typename, settype->typename); + restore_set->index = set_find_free_index(name); + restore_set->header_size = settype->create_size; + restore_set->members_size = 0; + + DP("name %s, restore index %u", restore_set->name, restore_set->index); + /* Add settype data */ + + memcpy(restore_data + restore_offset + sizeof(struct ip_set_restore), + settype->data, settype->create_size); - if (res != 0) - kernel_error(errno); + restore_offset += sizeof(struct ip_set_restore) + + settype->create_size; + + /* Add set to set_list */ + set = ipset_malloc(sizeof(struct set)); + strcpy(set->name, name); + set->settype = settype; + set->index = restore_set->index; + set_list[restore_set->index] = set; } -static int get_protocolversion(void) +/* + * Send destroy/flush order to kernel for one or all sets + */ +static void set_destroy(const char *name, unsigned op, unsigned cmd) { - struct ip_set_req_version req_version; - size_t size = sizeof(struct ip_set_req_version); + struct ip_set_req_std req; - req_version.op = IP_SET_OP_VERSION; - - kernel_getfrom(&req_version, &size); + DP("%s %s", cmd == CMD_DESTROY ? "destroy" : "flush", name); - DP("get_protocolversion() ver=%d", req_version.version); - - return req_version.version; -} + req.op = op; + req.version = IP_SET_PROTOCOL_VERSION; + strcpy(req.name, name); -static void set_append(struct set *set) -{ - struct set *entry = all_sets; - - while (entry != NULL && entry->next != NULL) - entry = entry->next; - - if (entry == NULL) - all_sets = set; - else - entry->next = set; + kernel_sendto(cmd, &req, sizeof(struct ip_set_req_std)); } -static void get_sets(void) +/* + * Send rename/swap order to kernel + */ +static void set_rename(const char *name, const char *newname, + unsigned op, unsigned cmd) { - void *data = NULL; - struct ip_set_req_listing_size req_list; - int sockfd = kernel_getsocket(); - size_t size; - size_t eaten = 0; - int i, res; + struct ip_set_req_create req; - DP("get_sets()"); - for (i = 0; i < LIST_TRIES; i++) { - req_list.op = IP_SET_OP_LISTING_SIZE; - size = sizeof(req_list); - kernel_getfrom(&req_list, &size); - size = req_list.size; + DP("%s %s %s", cmd == CMD_RENAME ? "rename" : "swap", + name, newname); - DP("got size: %d", size); + req.op = op; + req.version = IP_SET_PROTOCOL_VERSION; + strcpy(req.name, name); + strcpy(req.typename, newname); - if (req_list.size == 0) - return; /* No sets in kernel */ + kernel_sendto(cmd, &req, + sizeof(struct ip_set_req_create)); +} - data = ipset_malloc(size); - ((struct ip_set_req_base *) data)->op = IP_SET_OP_LISTING; +/* + * Send MAX_SETS, LIST_SIZE and/or SAVE_SIZE orders to kernel + */ +static size_t load_set_list(const char name[IP_SET_MAXNAMELEN], + ip_set_id_t *index, + unsigned op, unsigned cmd) +{ + void *data = NULL; + struct ip_set_req_max_sets req_max_sets; + struct ip_set_name_list *name_list; + struct set *set; + ip_set_id_t i; + size_t size, req_size; + int repeated = 0, res = 0; + + DP("%s %s", cmd == CMD_MAX_SETS ? "MAX_SETS" + : cmd == CMD_LIST_SIZE ? "LIST_SIZE" + : "SAVE_SIZE", + name); + +tryagain: + if (set_list) { + for (i = 0; i < max_sets; i++) + if (set_list[i]) + free(set_list[i]); + free(set_list); + set_list = NULL; + } + /* Get max_sets */ + req_max_sets.op = IP_SET_OP_MAX_SETS; + req_max_sets.version = IP_SET_PROTOCOL_VERSION; + strcpy(req_max_sets.set.name, name); + size = sizeof(req_max_sets); + kernel_getfrom(CMD_MAX_SETS, &req_max_sets, &size); + + DP("got MAX_SETS: sets %d, max_sets %d", + req_max_sets.sets, req_max_sets.max_sets); + + max_sets = req_max_sets.max_sets; + set_list = ipset_malloc(max_sets * sizeof(struct set *)); + memset(set_list, 0, max_sets * sizeof(struct set *)); + *index = req_max_sets.set.index; + + if (req_max_sets.sets == 0) + /* No sets in kernel */ + return 0; - /* Get! */ - res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, &size); - DP("list_get getsockopt() res=%d errno=%d\n", res, errno); + /* Get setnames */ + size = req_size = sizeof(struct ip_set_req_setnames) + + req_max_sets.sets * sizeof(struct ip_set_name_list); + data = ipset_malloc(size); + ((struct ip_set_req_setnames *) data)->op = op; + ((struct ip_set_req_setnames *) data)->index = *index; - if (res == 0) - goto got_sets; /* all OK */ - else if (errno != ENOMEM) - break; /* Not a memory error */ + res = kernel_getfrom_handleerrno(cmd, data, &size); - DP("not enough mem, looping again"); + if (res != 0 || size != req_size) { free(data); - } - - if (errno == ENOMEM) + if (repeated++ < LIST_TRIES) + goto tryagain; exit_error(OTHER_PROBLEM, - "Tried to list sets from kernel %d times" + "Tried to get sets from kernel %d times" " and failed. Please try again when the load on" " the sets has gone down.", LIST_TRIES); - else - kernel_error(errno); - - got_sets: - - DP("get_sets() - size=%d data=%p", size, data); - /* Handle the data */ - while (eaten < size) { - struct ip_set_req_listing *header = - (struct ip_set_req_listing *) (data + eaten); - struct set *set = ipset_malloc(sizeof(struct set)); - - memset(set, 0, sizeof(struct set)); + } - DP("fillin %d %p", eaten, header); - -#ifdef IP_SET_DEBUG - /* DEBUG */ - { - void *i; - - DP("SET DATA:"); + /* Load in setnames */ + size = sizeof(struct ip_set_req_setnames); + while (size + sizeof(struct ip_set_name_list) <= req_size) { + name_list = (struct ip_set_name_list *) + (data + size); + set = ipset_malloc(sizeof(struct set)); + strcpy(set->name, name_list->name); + set->index = name_list->index; + set->settype = settype_load(name_list->typename); + set_list[name_list->index] = set; + DP("loaded %s, type %s, index %u", + set->name, set->settype->typename, set->index); + size += sizeof(struct ip_set_name_list); + } + /* Size to get set members, bindings */ + size = ((struct ip_set_req_setnames *)data)->size; + free(data); + + return size; +} - for (i = data + eaten; - i < - data + eaten + - sizeof(struct ip_set_req_listing); i += 4) { - unsigned *j = (unsigned *) i; - DP("%x", *j); - } - } -#endif +/* + * Save operation + */ +static size_t save_bindings(void *data, size_t offset, size_t len) +{ + struct ip_set_hash_save *hash = + (struct ip_set_hash_save *) (data + offset); + struct set *set; + char * (*printip)(ip_set_ip_t ip, unsigned options); - /* Fill in data */ - DP("Processing set '%s'", header->name); - strcpy(set->name, header->name); - set->id = header->id; - set->levels = header->levels; - set->ref = header->ref; - for (i = 0; i < set->levels; i++) { - set->settype[i] = settype_load(header->typename[i]); - set->private[i].adt = - ipset_malloc(set->settype[i]->req_size); - } + DP("offset %u, len %u", offset, len); + if (offset + sizeof(struct ip_set_hash_save) > len) + exit_error(OTHER_PROBLEM, + "Save operation failed, try again later."); - eaten += sizeof(struct ip_set_req_listing); + set = set_find_byid(hash->id); + if (!(set && set_list[hash->binding])) + exit_error(OTHER_PROBLEM, + "Save binding failed, try again later."); + printip = set->settype->typecode == IPSET_TYPE_IP ? + ip_tostring : port_tostring; + printf("-B %s %s -b %s\n", + set->name, + printip(hash->ip, OPT_NUMERIC), + set_list[hash->binding]->name); + + return sizeof(struct ip_set_hash_save); +} + +static size_t save_set(void *data, int *bindings, + size_t offset, size_t len) +{ + struct ip_set_save *set_save = + (struct ip_set_save *) (data + offset); + struct set *set; + struct settype *settype; + size_t used; + + DP("offset %u, len %u", offset, len); + if (offset + sizeof(struct ip_set_save) > len + || offset + sizeof(struct ip_set_save) + + set_save->header_size + set_save->members_size > len) + exit_error(OTHER_PROBLEM, + "Save operation failed, try again later."); - DP("insert in list"); - set_append(set); + if (set_save->index == IP_SET_INVALID_ID) { + /* Marker */ + *bindings = 1; + return sizeof(struct ip_set_save); } + set = set_list[set_save->index]; + if (!set) + exit_error(OTHER_PROBLEM, + "Save set failed, try again later."); + settype = set->settype; - DP("free"); + /* Init set header */ + used = sizeof(struct ip_set_save); + settype->initheader(set, data + offset + used); - free(data); + /* Print create set */ + settype->saveheader(set, OPT_NUMERIC); - DP("get_sets() eaten = %d, size = %d", eaten, size); + /* Print add IPs */ + used += set_save->header_size; + settype->saveips(set, data + offset + used, + set_save->members_size, OPT_NUMERIC); - if (eaten != size) - exit_error(OTHER_PROBLEM, - "Desynched in listing of sets from kernel. " - "Read %d bytes. Worked %d bytes.", size, eaten); + return (used + set_save->members_size); } -struct set *set_find(const char *setname) +static size_t save_default_bindings(void *data, int *bindings) { - struct set *set = all_sets; + struct ip_set_save *set_save = (struct ip_set_save *) data; + struct set *set; + + if (set_save->index == IP_SET_INVALID_ID) { + /* Marker */ + *bindings = 1; + return sizeof(struct ip_set_save); + } - DP("%s", setname); + set = set_list[set_save->index]; + DP("%s, binding %u", set->name, set_save->binding); + if (set_save->binding != IP_SET_INVALID_ID) { + if (!set_list[set_save->binding]) + exit_error(OTHER_PROBLEM, + "Save set failed, try again later."); - while (set != NULL) { - if (strcmp(setname, set->name) == 0) - return set; - set = set->next; + printf("-B %s %s -b %s\n", + set->name, IPSET_TOKEN_DEFAULT, + set_list[set_save->binding]->name); } - return NULL; + return (sizeof(struct ip_set_save) + + set_save->header_size + + set_save->members_size); } -static struct set *set_checkname(const char *setname) +static int try_save_sets(const char name[IP_SET_MAXNAMELEN]) { - char *saved = strdup(setname); - char *ptr, *tmp = saved; - struct set *set; - int i; - - DP("%s", setname); - - /* Cleanup */ - if (set_name != NULL) - free(set_name); + void *data = NULL; + size_t size, req_size = 0; + ip_set_id_t index; + int res = 0, bindings = 0; + time_t now = time(NULL); + + /* Load set_list from kernel */ + size = load_set_list(name, &index, + IP_SET_OP_SAVE_SIZE, CMD_SAVE); - for (i = 0; i < IP_SET_SETIP_LEVELS; i++) - set_ip[i] = 0; - set_level = 0; + if (size) { + /* Get sets, bindings and print them */ + /* Take into account marker */ + req_size = (size += sizeof(struct ip_set_save)); + data = ipset_malloc(size); + ((struct ip_set_req_list *) data)->op = IP_SET_OP_SAVE; + ((struct ip_set_req_list *) data)->index = index; + res = kernel_getfrom_handleerrno(CMD_SAVE, data, &size); - /* name[:ip,...] */ - ptr = strsep(&tmp, ":"); - if (strlen(ptr) > IP_SET_MAXNAMELEN - 1) - exit_error(PARAMETER_PROBLEM, - "Setname '%s' in '%s' too long. Max %d characters.", - ptr, setname, IP_SET_MAXNAMELEN - 1); - - DP("%s (%s)", ptr, tmp); - set = set_find(ptr); - if (!set && tmp) - exit_error(PARAMETER_PROBLEM, - "Set '%s' not found for '%s'\n", - ptr, setname); - - set_name = strdup(ptr); - - while (set_level < IP_SET_SETIP_LEVELS && tmp) { - ptr = strsep(&tmp, ","); - switch (set->settype[set_level]->typecode) { - case IPSET_TYPE_IP: - parse_ip(ptr, &set_ip[set_level++]); - break; - case IPSET_TYPE_PORT: - parse_port(ptr, &set_ip[set_level++]); - break; - default: - ; /* Keep compilers happy */ - } - if (set->levels <= set_level) - exit_error(PARAMETER_PROBLEM, - "Subset definition is too deep for set '%s'\n", - set_name); + if (res != 0 || size != req_size) { + free(data); + return -EAGAIN; + } } - free(saved); - return set; -} - -static inline struct set *set_find_byname(const char *setname) -{ - struct set *set = set_checkname(setname); - - if (!set) - exit_error(PARAMETER_PROBLEM, - "Set '%s' not found\n", setname); - return set; -} - -static void set_checktype(const char *typename) -{ - char *saved = strdup(typename); - char *ptr, *tmp = saved; - int i; - - /* Cleanup */ - for (i = 0; i < IP_SET_LEVELS; i++) - if (set_typename[i] != NULL) - free(set_typename[i]); - - /* typename[,...] */ - set_typename_level = 0; - while (set_typename_level < IP_SET_LEVELS && tmp) { - ptr = strsep(&tmp, ","); - DP("settype '%s', level %i", ptr, set_typename_level); - if (strlen(ptr) > IP_SET_MAXNAMELEN - 1) - exit_error(PARAMETER_PROBLEM, - "Typename '%s' in '%s' too long. Max %d characters.", - ptr, typename, IP_SET_MAXNAMELEN - 1); - set_typename[set_typename_level++] = strdup(ptr); + printf("# Generated by ipset %s on %s", IPSET_VERSION, ctime(&now)); + size = 0; + while (size < req_size) { + DP("size: %u, req_size: %u", size, req_size); + if (bindings) + size += save_bindings(data, size, req_size); + else + size += save_set(data, &bindings, size, req_size); } - DP("tmp '%s', level %i", tmp, set_typename_level); - if (set_typename_level >= IP_SET_LEVELS || tmp) - exit_error(PARAMETER_PROBLEM, - "More than %d settypes in '%s'.", - IP_SET_LEVELS - 1 , typename); - free(saved); + /* Re-read data to save default bindings */ + bindings = 0; + size = 0; + while (size < req_size && bindings == 0) + size += save_default_bindings(data + size, &bindings); + + printf("COMMIT\n"); + now = time(NULL); + printf("# Completed on %s", ctime(&now)); + ipset_free(&data); + return res; } -/* Get setid from kernel. */ -static void -set_get_setid(struct set *set) +/* + * Performs a save to stdout + */ +static void set_save(const char name[IP_SET_MAXNAMELEN]) { - struct ip_set_req_get req_get; - size_t size = sizeof(struct ip_set_req_get); - - DP("set_get_setid()"); - - req_get.op = IP_SET_OP_GETSET_BYNAME; - strcpy(req_get.name, set->name); - - /* Result in the id-field */ - kernel_getfrom(&req_get, &size); - - if (size != sizeof(struct ip_set_req_get)) - exit_error(OTHER_PROBLEM, - "Incorrect return size from kernel." - "Should be %d but was %d.", - sizeof(struct ip_set_req_get), size); + int i; - DP("set_get_setid() result=%u", req_get.id); + DP("%s", name); + for (i = 0; i < LIST_TRIES; i++) + if (try_save_sets(name) == 0) + return; - if (req_get.id < 0) + if (errno == EAGAIN) exit_error(OTHER_PROBLEM, - "Set %s cannot be found.", - set->name); - - set->id = req_get.id; - set->ref = req_get.ref; + "Tried to save sets from kernel %d times" + " and failed. Please try again when the load on" + " the sets has gone down.", LIST_TRIES); + else + kernel_error(CMD_SAVE, errno); } -/* Send create set order to kernel */ -static void -set_create(struct set *set) -{ - struct ip_set_req_create req_create; - struct settype *settype = set->settype[0]; - size_t size; - void *data; - int i; - - DP("set_create()"); - - req_create.op = IP_SET_OP_CREATE; - strcpy(req_create.name, set->name); - for (i = 0; i < set_typename_level; i++) - strcpy(req_create.typename[i], set->settype[i]->typename); - req_create.levels = set_typename_level; - - /* Final checks */ - settype->create_final(settype->data, settype->flags); - - /* Alloc memory for the data to send */ - size = sizeof(struct ip_set_req_create) + settype->create_size; - data = ipset_malloc(size); - - /* Add up ip_set_req_create and the settype data */ - memcpy(data, &req_create, sizeof(struct ip_set_req_create)); - memcpy(data + sizeof(struct ip_set_req_create), - settype->data, settype->create_size); - -#ifdef IP_SET_DEBUG - /* DEBUG */ - { - void *i; - - DP("CMD_CREATE"); - DP("OP %u", req_create.op); - DP("Name: %s", req_create.name); - DP("Typename: %s", req_create.typename[0]); - DP("All data"); - - for (i = data; i < data + size; i += 4) { - unsigned *j = (unsigned *) i; - DP("%x", *j); - } - } -#endif +/* + * Restore operation + */ - kernel_sendto(data, size); - free(data); - - /* Get set id from kernel */ - set_get_setid(set); - /* Success! */ - set_append(set); -} +/* global new argv and argc */ +static char *newargv[255]; +static int newargc = 0; -/* Send create childset order to kernel */ -static void -set_create_childset(const struct set *set, unsigned options) -{ - struct ip_set_req_sub req_sub; - struct settype *settype = set->settype[set_level]; - size_t size; - void *data; +/* Build faked argv from parsed line */ +static void build_argv(int line, char *buffer) { + char *ptr; int i; - DP("childset_create()"); - - req_sub.op = IP_SET_OP_CREATE_CHILD; - req_sub.id = set->id; - for (i = 0; i < set_level; i++) - req_sub.ip[i] = set_ip[i]; - req_sub.level = set_level; - req_sub.childsets = options & OPT_CHILDSETS; - - /* Final checks */ - settype->create_final(settype->data, settype->flags); - - /* Alloc memory for the data to send */ - size = sizeof(struct ip_set_req_sub) + settype->create_size; - data = ipset_malloc(size); - - /* Add up ip_set_req_sub and the settype data */ - memcpy(data, &req_sub, sizeof(struct ip_set_req_sub)); - memcpy(data + sizeof(struct ip_set_req_sub), - settype->data, settype->create_size); + /* Reset */ + for (i = 1; i < newargc; i++) + free(newargv[i]); + newargc = 1; -#ifdef IP_SET_DEBUG - /* DEBUG */ - { - void *i; - - DP("CMD_CREATE_CHILD"); - DP("OP %u", req_sub.op); - DP("Id: %u", req_sub.id); - DP("All data"); - - for (i = data; i < data + size; i += 4) { - unsigned *j = (unsigned *) i; - DP("%x", *j); - } - } -#endif - - kernel_sendto(data, size); - free(data); -} - -static void set_del(struct set *set) -{ - int i; - - for (i = 0; i < set->levels; i++) { - if (set->private[i].setdata) - set->settype[i]->killmembers(&set->private[i].setdata); - ipset_free(&set->private[i].bitmap); + ptr = strtok(buffer, " \t\n"); + newargv[newargc++] = strdup(ptr); + while ((ptr = strtok(NULL, " \t\n")) != NULL) { + if ((newargc + 1) < sizeof(newargv)/sizeof(char *)) + newargv[newargc++] = strdup(ptr); + else + exit_error(PARAMETER_PROBLEM, + "Line %d is too long to restore\n", line); } - - free(set); } -/* Sends destroy order to kernel for one or all sets - * All sets: set == NULL +/* + * Performs a restore from a file */ -static void set_destroy(struct set *set) +static void set_restore(FILE *in, char *argv0) { - struct ip_set_req_std req_destroy; - - req_destroy.op = IP_SET_OP_DESTROY; - - if (set == NULL) { - /* Do them all */ - - while (all_sets != NULL) { - set = all_sets; - all_sets = set->next; - req_destroy.id = set->id; - req_destroy.level = 0; - kernel_sendto(&req_destroy, - sizeof(struct ip_set_req_std)); - set_del(set); + char buffer[1024]; + char *ptr, *name = NULL; + char cmd = ' '; + int line = 0, first_pass, i, bindings = 0; + struct settype *settype = NULL; + struct ip_set_req_setnames *header; + ip_set_id_t index; + int res; + + /* Load existing sets from kernel */ + load_set_list(IPSET_TOKEN_ALL, &index, + IP_SET_OP_LIST_SIZE, CMD_RESTORE); + + restore_size = sizeof(struct ip_set_req_setnames)/* header */ + + sizeof(struct ip_set_restore); /* marker */ + DP("restore_size: %u", restore_size); + /* First pass: calculate required amount of data */ + while (fgets(buffer, sizeof(buffer), in)) { + line++; + + if (buffer[0] == '\n') + continue; + else if (buffer[0] == '#') + continue; + else if (strcmp(buffer, "COMMIT\n") == 0) { + /* Enable restore mode */ + restore = 1; + break; } - } else { - int i; - - DP("destroy %s", set->name); - - /* Only destroy one */ - req_destroy.id = set->id; - for (i = 0; i < set_level; i++) - req_destroy.ip[i] = set_ip[i]; - req_destroy.level = set_level; - kernel_sendto(&req_destroy, - sizeof(struct ip_set_req_std)); - if (set_level == 0) { - if (set == all_sets) - all_sets = set->next; - else { - struct set *entry = all_sets; - while (entry && entry->next && entry->next != set) - entry = entry->next; - if (entry->next == set) - entry->next = set->next; - } - set_del(set); + /* -N, -A or -B */ + ptr = strtok(buffer, " \t\n"); + DP("ptr: %s", ptr); + if (ptr == NULL + || ptr[0] != '-' + || !(ptr[1] == 'N' + || ptr[1] == 'A' + || ptr[1] == 'B') + || ptr[2] != '\0') { + exit_error(PARAMETER_PROBLEM, + "Line %u does not start as a valid restore command\n", + line); } - } -} - -/* Sends flush order to kernel for one or all sets - * All sets: set = NULL - */ -static void set_flush(const struct set *set, unsigned options) -{ - struct ip_set_req_sub req_flush; - - DP("flush"); - - req_flush.op = IP_SET_OP_FLUSH; - - if (set == NULL) { - /* Do them all */ - struct set *entry = all_sets; - - while (entry != NULL) { - req_flush.id = entry->id; - req_flush.level = 0; - req_flush.childsets = options & OPT_CHILDSETS; - kernel_sendto(&req_flush, - sizeof(struct ip_set_req_sub)); - entry = entry->next; + cmd = ptr[1]; + /* setname */ + ptr = strtok(NULL, " \t\n"); + DP("setname: %s", ptr); + if (ptr == NULL) + exit_error(PARAMETER_PROBLEM, + "Missing set name in line %u\n", + line); + DP("cmd %c", cmd); + switch (cmd) { + case 'N': { + name = check_set_name(ptr); + /* settype */ + ptr = strtok(NULL, " \t\n"); + if (ptr == NULL) + exit_error(PARAMETER_PROBLEM, + "Missing settype in line %u\n", + line); + if (restore) + exit_error(PARAMETER_PROBLEM, + "Invalid line %u: create must precede bindings\n", + line); + settype = check_set_typename(ptr); + restore_size += sizeof(struct ip_set_restore) + + settype->create_size; + DP("restore_size (N): %u", restore_size); + break; } - } else { - int i; - - /* Only one */ - req_flush.id = set->id; - for (i = 0; i < set_level; i++) - req_flush.ip[i] = set_ip[i]; - req_flush.level = set_level; - req_flush.childsets = options & OPT_CHILDSETS; - kernel_sendto(&req_flush, sizeof(struct ip_set_req_sub)); - } -} - -/* Sends rename order to kernel */ -static void set_rename(struct set *set, const char *newname) -{ - struct ip_set_req_rename req_rename; - - DP("rename"); - - req_rename.op = IP_SET_OP_RENAME; - req_rename.id = set->id; - strncpy(req_rename.newname, newname, IP_SET_MAXNAMELEN); - - DP("rename - send"); - kernel_sendto(&req_rename, sizeof(struct ip_set_req_rename)); + case 'A': { + if (strncmp(name, ptr, sizeof(name)) != 0) + exit_error(PARAMETER_PROBLEM, + "Add IP to set %s in line %u without " + "preceding corresponding create set line\n", + ptr, line); + if (restore) + exit_error(PARAMETER_PROBLEM, + "Invalid line %u: adding entries must precede bindings\n", + line); + restore_size += settype->adt_size; + DP("restore_size (A): %u", restore_size); + break; + } + case 'B': { + bindings = 1; + restore_size += sizeof(struct ip_set_hash_save); + DP("restore_size (B): %u", restore_size); + break; + } + default: { + exit_error(PARAMETER_PROBLEM, + "Unrecognized restore command in line %u\n", + line); + } + } /* end of switch */ + } + /* Sanity checking */ + if (!restore) + exit_error(PARAMETER_PROBLEM, + "Missing COMMIT line\n"); + DP("restore_size: %u", restore_size); + restore_data = ipset_malloc(restore_size); + header = (struct ip_set_req_setnames *) restore_data; + header->op = IP_SET_OP_RESTORE; + header->size = restore_size; + restore_offset = sizeof(struct ip_set_req_setnames); + + /* Rewind to scan the file again */ + res = fseek(in, 0L, SEEK_SET); + if (res) + exit_error(PARAMETER_PROBLEM, + "Cannot rewind stdin: %s", strerror(errno)); + first_pass = line; + line = 0; - strncpy(set->name, newname, IP_SET_MAXNAMELEN); -} - -/* Sends swap order to kernel for two sets */ -static void set_swap(struct set *from, struct set *to) -{ - struct ip_set_req_swap req_swap; - - DP("swap"); - - req_swap.op = IP_SET_OP_SWAP; - req_swap.id = from->id; - req_swap.to = to->id; - - DP("swap - send"); - kernel_sendto(&req_swap, sizeof(struct ip_set_req_swap)); + /* Initialize newargv/newargc */ + newargv[newargc++] = strdup(argv0); - from->id = req_swap.to; - to->id = req_swap.id; -} - -static void *list_get(int opsize, int opget, ip_set_ip_t id, - int *size, ip_set_ip_t *ip, unsigned level) -{ - int res, i; - struct ip_set_req_list req_list; - struct ip_set_req_std *req_data; - int sockfd = kernel_getsocket(); - void *data = NULL; + /* Second pass: build up restore request */ + while (fgets(buffer, sizeof(buffer), in)) { + line++; - req_list.op = opsize; - req_list.id = id; - req_list.level = level; - for (i = 0; i < level; i++) - req_list.ip[i] = ip[i]; - - *size = sizeof(req_list); - kernel_getfrom(&req_list, size); - - DP("got size: %d", req_list.size); - - if (req_list.size == 0) - return data; - - *size = sizeof(struct ip_set_req_std) > req_list.size ? - sizeof(struct ip_set_req_std) : req_list.size; - - data = ipset_malloc(*size); - req_data = (struct ip_set_req_std *) data; - req_data->op = opget; - req_data->id = id; - req_data->level = level; - for (i = 0; i < level; i++) - req_data->ip[i] = ip[i]; - - /* Get! */ - res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); - DP("list_get getsockopt() res=%d errno=%d\n", res, errno); - - if (res != 0) - kernel_error(errno); + if (buffer[0] == '\n') + continue; + else if (buffer[0] == '#') + continue; + else if (strcmp(buffer, "COMMIT\n") == 0) + goto do_restore; + DP("restoring: %s", buffer); + /* Build faked argv, argc */ + build_argv(line, buffer); + for (i = 0; i < newargc; i++) + DP("argv[%u]: %s", i, newargv[i]); + + /* Parse line */ + parse_commandline(newargc, newargv); + } + exit_error(PARAMETER_PROBLEM, + "Broken restore file\n"); + do_restore: + if (bindings == 0 + && restore_size == + (restore_offset + sizeof(struct ip_set_restore))) { + /* No bindings */ + struct ip_set_restore *marker = + (struct ip_set_restore *) (restore_data + restore_offset); + + DP("restore marker"); + marker->index = IP_SET_INVALID_ID; + marker->header_size = marker->members_size = 0; + restore_offset += sizeof(struct ip_set_restore); + } + if (restore_size != restore_offset) + exit_error(PARAMETER_PROBLEM, + "Giving up, restore file is screwed up!"); + res = kernel_getfrom_handleerrno(CMD_RESTORE, restore_data, &restore_size); - /* All OK, return */ - return data; + if (res != 0) { + if (restore_size != sizeof(struct ip_set_req_setnames)) + exit_error(PARAMETER_PROBLEM, + "Communication with kernel failed (%u %u)!", + restore_size, sizeof(struct ip_set_req_setnames)); + /* Check errors */ + header = (struct ip_set_req_setnames *) restore_data; + if (header->size != 0) + exit_error(PARAMETER_PROBLEM, + "Committing restoring failed at line %u!", + header->size); + } } -static void list_getheaders(struct set *set, ip_set_ip_t *ip, unsigned level) +/* + * Send ADT_GET order to kernel for a set + */ +static struct set *set_adt_get(const char *name) { - void *data; + struct ip_set_req_adt_get req_adt_get; + struct set *set; size_t size; - data = list_get(IP_SET_OP_LIST_HEADER_SIZE, - IP_SET_OP_LIST_HEADER, - set->id, &size, ip, level); + DP("%s", name); - if (size == 0) - exit_error(OTHER_PROBLEM, - "Kernel returned zero header size. " - "Something screwed up."); - - /* Cleanup setdata */ - if (set->private[level].setdata) - set->settype[level]->killmembers(&set->private[level].setdata); + req_adt_get.op = IP_SET_OP_ADT_GET; + req_adt_get.version = IP_SET_PROTOCOL_VERSION; + strcpy(req_adt_get.set.name, name); + size = sizeof(struct ip_set_req_adt_get); - /* Handle the data */ - set->settype[level]->initheader(&set->private[level].setdata, - data, size); + kernel_getfrom(CMD_ADT_GET, &req_adt_get, &size); - ipset_free(&data); -} - -static void list_getmembers(struct set *set, ip_set_ip_t *ip, unsigned level) -{ - void *data; - size_t size; + set = ipset_malloc(sizeof(struct set)); + strcpy(set->name, name); + set->index = req_adt_get.set.index; + set->settype = settype_load(req_adt_get.typename); - data = list_get(IP_SET_OP_LIST_MEMBERS_SIZE, - IP_SET_OP_LIST_MEMBERS, - set->id, &size, ip, level); - - if (size == 0) - exit_error(OTHER_PROBLEM, - "Kernel returned zero header size. " - "Something screwed up."); - - /* Handle the data */ - set->settype[level]->initmembers(set->private[level].setdata, - data, size); - - /* Next list_getheaders or set_del frees the data */ -} + return set; +} -static void list_getchildsets(struct set *set, ip_set_ip_t *ip, unsigned level) +/* + * Send add/del/test order to kernel for a set + */ +static int set_adtip(struct set *set, const char *adt, + unsigned op, unsigned cmd) { - void *data; + struct ip_set_req_adt *req_adt; size_t size; + void *data; + int res = 0; - data = list_get(IP_SET_OP_LIST_CHILDSETS_SIZE, - IP_SET_OP_LIST_CHILDSETS, - set->id, &size, ip, level); - - /* Cleanup */ - ipset_free(&set->private[level].bitmap); - set->private[level].bitmap = NULL; - - if (size == 0) - return; /* No child set */ - - /* Handle the data */ - set->private[level].bitmap = data; + DP("%s -> %s", set->name, adt); - /* Next list_getchildsets or set_del frees the data */ -} + /* Alloc memory for the data to send */ + size = sizeof(struct ip_set_req_adt) + set->settype->adt_size ; + DP("alloc size %i", size); + data = ipset_malloc(size); -/* Print a child set */ -static void set_list_childset(struct set *set, - unsigned options, - ip_set_ip_t *ip, - unsigned level) -{ - int i; - ip_set_ip_t id; + /* Fill out the request */ + req_adt = (struct ip_set_req_adt *) data; + req_adt->op = op; + req_adt->index = set->index; + memcpy(data + sizeof(struct ip_set_req_adt), + set->settype->data, set->settype->adt_size); - /* Load the set header, member and childset data */ - list_getheaders(set, ip, level); - list_getmembers(set, ip, level); - list_getchildsets(set, ip, level); - - /* Pretty print the childset */ - printf("Childset %s %s", - set->private[level].bitmap != NULL ? "+" : "-", - set->name); - for (i = 0; i < level; i++) { - switch (set->settype[i]->typecode) { - case IPSET_TYPE_IP: - printf("%s%s", i == 0 ? ":" : ",", - ip_tostring(ip[i], options)); + if (kernel_sendto_handleerrno(cmd, op, data, size) == -1) + switch (op) { + case IP_SET_OP_ADD_IP: + exit_error(OTHER_PROBLEM, "%s is already in set %s.", + adt, set->name); break; - case IPSET_TYPE_PORT: - printf("%s%s", i == 0 ? ":" : ",", - port_tostring(ip[i], options)); + case IP_SET_OP_DEL_IP: + exit_error(OTHER_PROBLEM, "%s is not in set %s.", + adt, set->name); + break; + case IP_SET_OP_TEST_IP: + ipset_printf("%s is in set %s.", adt, set->name); + res = 0; break; default: - ; + break; } - } - printf("\n"); - - /* Pretty print the type header */ - set->settype[level]->printheader(set->private[level].setdata, options); - - /* Pretty print all IPs */ - if (options & OPT_SORTED) - set->settype[level]->printips_sorted(set->private[level].setdata, options); else - set->settype[level]->printips(set->private[level].setdata, options); - - /* Pretty print all childsets. */ - if (!(set->private[level].bitmap != NULL && (options & OPT_CHILDSETS))) - return; - - for (id = 0; - id < set->settype[level]->sizeid(set->private[level].setdata); - id++) { - if (test_bit(id, set->private[level].bitmap)) { - ip[level] = set->settype[level]->getipbyid( - set->private[level].setdata, id); - set_list_childset(set, options, ip, level+1); + switch (op) { + case IP_SET_OP_TEST_IP: + ipset_printf("%s is NOT in set %s.", adt, set->name); + res = 1; + break; + default: + break; } - } -} - -/* Help function to set_list() */ -static void set_list_oneset(struct set *set, - unsigned options) -{ - int i; - - /* Pretty print the set */ - printf("Name %s\n", set->name); - printf("Type %s", set->settype[0]->typename); - for (i = 1; i < set->levels; i++) - printf(",%s", set->settype[i]->typename); - printf("\n"); - printf("References %d\n", set->ref); - - /* Pretty print the childset */ - set_list_childset(set, options, set_ip, set_level); + free(data); - printf("\n"); /* One newline between sets */ + return res; } -/* Print a set or all sets - * All sets: set = NULL - */ -static void set_list(struct set *set, unsigned options) +static void set_restore_add(struct set *set, const char *adt) { - if (set == NULL) { - set = all_sets; - - while (set != NULL) { - set_list_oneset(set, options); - set = set->next; - } - } else - set_list_oneset(set, options); + DP("%s %s", set->name, adt); + /* Sanity checking */ + if (restore_offset + set->settype->adt_size > restore_size) + exit_error(PARAMETER_PROBLEM, + "Giving up, restore file is screwed up!"); + + memcpy(restore_data + restore_offset, + set->settype->data, set->settype->adt_size); + restore_set->members_size += set->settype->adt_size; + restore_offset += set->settype->adt_size; } -/* Performs a save to stdout - * All sets are marked with set.name[0] == '\0' - * Area pointed by 'set' will be changed +/* + * Send bind/unbind/test binding order to kernel for a set */ -static void set_save(const struct set *set) +static int set_bind(struct set *set, const char *adt, + const char *binding, + unsigned op, unsigned cmd) { - DP("set_save() not yet implemented"); -} - -/* Performs a restore from stdin */ -static void set_restore() -{ - DP("set_restore() not yet implemented"); -} - -static void parse_adt_ip(int cmd, struct set *set, const char *adt_ip) -{ - char *saved = strdup(adt_ip); - char *ptr, *tmp = saved; - - ip_level = set_level; - - while (ip_level <= set->levels && tmp) { - - if (ip_level > set_level) - list_getheaders(set, set_ip, ip_level); - - ptr = strsep(&tmp, ","); - - /* Call the parser function */ - set_ip[ip_level] = set->settype[ip_level]->adt_parser( - cmd, ptr, - set->private[ip_level].adt, - set->private[ip_level].setdata); - DP("%i: (%s)", ip_level, ip_tostring(set_ip[ip_level], 0)); - ip_level++; - } - - if (tmp || ip_level > set->levels) - exit_error(PARAMETER_PROBLEM, - "Specified (child) set and IP levels together" - " are deeper than %s set (%i).", - set->name, set->levels); - - free(saved); -} - -/* Sends addip order to kernel for a set */ -static void set_addip(struct set *set, const char *adt_ip) -{ - struct ip_set_req_std req_addip; - size_t size, offset; + struct ip_set_req_bind *req_bind; + size_t size; void *data; - int i; - - DP("set_addip() %p", set); - - list_getheaders(set, set_ip, set_level); - - parse_adt_ip(ADT_ADD, set, adt_ip); + int res = 0; - req_addip.op = IP_SET_OP_ADD_IP; - req_addip.id = set->id; - for (i = 0; i < set_level; i++) - req_addip.ip[i] = set_ip[i]; - req_addip.level = set_level; + /* set may be null: '-U :all: :all:|:default:' */ + DP("(%s, %s) -> %s", set ? set->name : IPSET_TOKEN_ALL, adt, binding); - DP("%i %i", set_level, ip_level); /* Alloc memory for the data to send */ - DP("alloc"); - size = sizeof(struct ip_set_req_std); - for (i = set_level; i < ip_level; i++) { - size += set->settype[i]->req_size; - DP("i: %i, size: %u", i, size); - } + size = sizeof(struct ip_set_req_bind); + if (op != IP_SET_OP_UNBIND_SET && adt[0] == ':') + /* Set default binding */ + size += IP_SET_MAXNAMELEN; + else if (!(op == IP_SET_OP_UNBIND_SET && set == NULL)) + size += set->settype->adt_size; + DP("alloc size %i", size); data = ipset_malloc(size); - /* Add up req_addip and the settype data */ - DP("mem"); - memcpy(data, &req_addip, sizeof(struct ip_set_req_std)); - offset = sizeof(struct ip_set_req_std); - for (i = set_level; i < ip_level; i++) { - memcpy(data + offset, - set->private[i].adt, - set->settype[i]->req_size); - offset += set->settype[i]->req_size; + /* Fill out the request */ + req_bind = (struct ip_set_req_bind *) data; + req_bind->op = op; + req_bind->index = set ? set->index : IP_SET_INVALID_ID; + if (adt[0] == ':') { + /* ':default:' and ':all:' */ + strncpy(req_bind->binding, adt, IP_SET_MAXNAMELEN); + if (op != IP_SET_OP_UNBIND_SET && adt[0] == ':') + strncpy(data + sizeof(struct ip_set_req_bind), + binding, IP_SET_MAXNAMELEN); + } else { + strncpy(req_bind->binding, binding, IP_SET_MAXNAMELEN); + memcpy(data + sizeof(struct ip_set_req_bind), + set->settype->data, set->settype->adt_size); } -#ifdef IP_SET_DEBUG - /* DEBUG */ - { - void *i; + if (op == IP_SET_OP_TEST_BIND_SET) { + if (kernel_sendto_handleerrno(cmd, op, data, size) == -1) { + ipset_printf("%s in set %s is bound to %s.", + adt, set->name, binding); + res = 0; + } else { + ipset_printf("%s in set %s is NOT bound to %s.", + adt, set->name, binding); + res = 1; + } + } else + kernel_sendto(cmd, data, size); + free(data); - DP("CMD_ADDIP"); - DP("OP %u", req_addip.op); - DP("Id: %u", req_addip.id); - DP("All data"); + return res; +} - for (i = data; i < data + size; i += 4) { - unsigned *j = (unsigned *) i; - DP("%x", *j); - } +static void set_restore_bind(struct set *set, + const char *adt, + const char *binding) +{ + struct ip_set_hash_save *hash_restore; + + if (restore == 1) { + /* Marker */ + struct ip_set_restore *marker = + (struct ip_set_restore *) (restore_data + restore_offset); + + DP("restore marker"); + if (restore_offset + sizeof(struct ip_set_restore) + > restore_size) + exit_error(PARAMETER_PROBLEM, + "Giving up, restore file is screwed up!"); + marker->index = IP_SET_INVALID_ID; + marker->header_size = marker->members_size = 0; + restore_offset += sizeof(struct ip_set_restore); + restore = 2; } -#endif + /* Sanity checking */ + if (restore_offset + sizeof(struct ip_set_hash_save) > restore_size) + exit_error(PARAMETER_PROBLEM, + "Giving up, restore file is screwed up!"); + + hash_restore = (struct ip_set_hash_save *) (restore_data + restore_offset); + DP("%s -> %s", adt, binding); + if (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0) + hash_restore->ip = 0; + else if (set->settype->typecode == IPSET_TYPE_IP) + parse_ip(adt, &hash_restore->ip); + else + parse_port(adt, &hash_restore->ip); + hash_restore->id = set->index; + hash_restore->binding = (set_find_byname(binding))->index; + DP("id %u, ip %u, binding %u", + hash_restore->id, hash_restore->ip, hash_restore->binding); + restore_offset += sizeof(struct ip_set_hash_save); +} - if (kernel_sendto_handleexist(data, size) == -1) - /* Arghh, we can't get the commandline string anymore, oh well */ - exit_error(OTHER_PROBLEM, "Already added in set %s.", - set->name); - free(data); +/* + * Print operation + */ + +static void print_bindings(void *data, size_t size, unsigned options, + char * (*printip)(ip_set_ip_t ip, unsigned options)) +{ + size_t offset = 0; + struct ip_set_hash_list *hash; + + while (offset < size) { + hash = (struct ip_set_hash_list *) (data + offset); + printf("%s -> %s\n", + printip(hash->ip, options), + set_list[hash->binding]->name); + offset += sizeof(struct ip_set_hash_list); + } } -/* Sends delip order to kernel for a set */ -static void set_delip(struct set *set, const char *adt_ip, unsigned options) +/* Help function to set_list() */ +static size_t print_set(void *data, unsigned options) { - struct ip_set_req_std req_delip; - size_t size, offset; - void *data; - int i; + struct ip_set_list *setlist = (struct ip_set_list *) data; + struct set *set = set_list[setlist->index]; + struct settype *settype = set->settype; + size_t offset; - DP("set_delip()"); + /* Pretty print the set */ + printf("Name: %s\n", set->name); + printf("Type: %s\n", settype->typename); + printf("References: %d\n", setlist->ref); + printf("Default binding: %s\n", + setlist->binding == IP_SET_INVALID_ID ? "" + : set_list[setlist->binding]->name); - list_getheaders(set, set_ip, set_level); + /* Init header */ + offset = sizeof(struct ip_set_list); + settype->initheader(set, data + offset); - parse_adt_ip(ADT_DEL, set, adt_ip); + /* Pretty print the type header */ + printf("Header:"); + settype->printheader(set, options); - req_delip.op = IP_SET_OP_DEL_IP; - req_delip.id = set->id; - for (i = 0; i < set_level; i++) - req_delip.ip[i] = set_ip[i]; - req_delip.level = set_level; + /* Pretty print all IPs */ + printf("Members:\n"); + offset += setlist->header_size; + if (options & OPT_SORTED) + settype->printips_sorted(set, data + offset, + setlist->members_size, options); + else + settype->printips(set, data + offset, + setlist->members_size, options); - /* Alloc memory for the data to send */ - size = sizeof(struct ip_set_req_std); - for (i = set_level; i < ip_level; i++) - size += set->settype[i]->req_size; - data = ipset_malloc(size); + /* Print bindings */ + printf("Bindings:\n"); + offset += setlist->members_size; + print_bindings(data + offset, setlist->bindings_size, options, + settype->typecode == IPSET_TYPE_IP ? + ip_tostring : port_tostring); - /* Add up req_sub and the settype data */ - memcpy(data, &req_delip, sizeof(struct ip_set_req_std)); - offset = sizeof(struct ip_set_req_std); - for (i = set_level; i < ip_level; i++) { - memcpy(data + offset, - set->private[i].adt, - set->settype[i]->req_size); - offset += set->settype[i]->req_size; - } + printf("\n"); /* One newline between sets */ + + return (offset + setlist->bindings_size); +} -#ifdef IP_SET_DEBUG - /* DEBUG */ - { - void *i; +static int try_list_sets(const char name[IP_SET_MAXNAMELEN], + unsigned options) +{ + void *data = NULL; + ip_set_id_t index; + size_t size, req_size; + int res = 0; - DP("CMD_DELIP"); - DP("OP %u", req_delip.op); - DP("Id: %u", req_delip.id); - DP("All data"); + DP("%s", name); + /* Load set_list from kernel */ + size = req_size = load_set_list(name, &index, + IP_SET_OP_LIST_SIZE, CMD_LIST); - for (i = data; i < data + size; i += 4) { - unsigned *j = (unsigned *) i; - DP("%x", *j); + if (size) { + /* Get sets and print them */ + data = ipset_malloc(size); + ((struct ip_set_req_list *) data)->op = IP_SET_OP_LIST; + ((struct ip_set_req_list *) data)->index = index; + res = kernel_getfrom_handleerrno(CMD_LIST, data, &size); + DP("get_lists getsockopt() res=%d errno=%d", res, errno); + + if (res != 0 || size != req_size) { + free(data); + return -EAGAIN; } + size = 0; } -#endif + while (size != req_size) + size += print_set(data + size, options); - if (kernel_sendto_handleexist(data, size) == -1) - /* Arghh, we can't get the commandline string anymore, oh well */ - exit_error(OTHER_PROBLEM, "Doesn't exist in set %s.", - set->name); - free(data); + ipset_free(&data); + return res; } -/* Sends test order to kernel for a set */ -static int -set_testip(struct set *set, const char *name, const char *adt_ip) +/* Print a set or all sets + * All sets: name = NULL + */ +static void list_sets(const char name[IP_SET_MAXNAMELEN], unsigned options) { - int i, res; - struct ip_set_req_test req_test; - void *data; - size_t size, offset; - - list_getheaders(set, set_ip, set_level); - - parse_adt_ip(ADT_TEST, set, adt_ip); - - req_test.op = IP_SET_OP_TEST_IP; - req_test.id = set->id; - for (i = 0; i < set_level; i++) - req_test.ip[i] = set_ip[i]; - req_test.level = set_level; + int i; - /* Alloc memory for the data to send */ - size = sizeof(struct ip_set_req_test); - for (i = set_level; i < ip_level; i++) - size += set->settype[i]->req_size; - data = ipset_malloc(size); + DP("%s", name); + for (i = 0; i < LIST_TRIES; i++) + if (try_list_sets(name, options) == 0) + return; - /* Add up req_test and the settype data */ - memcpy(data, &req_test, sizeof(struct ip_set_req_test)); - offset = sizeof(struct ip_set_req_test); - for (i = set_level; i < ip_level; i++) { - memcpy(data + offset, - set->private[i].adt, - set->settype[i]->req_size); - offset += set->settype[i]->req_size; - } - /* Result in the op-field */ - kernel_getfrom(data, &size); - - if (size != sizeof(struct ip_set_req_test)) + if (errno == EAGAIN) exit_error(OTHER_PROBLEM, - "Incorrect return size from kernel." - "Should be %d but was %d.", - sizeof(struct ip_set_req_test), size); - - DP("set_testip() result=%x", req_test.op); - - if (((struct ip_set_req_test *)data)->reply > 0) { - ipset_printf("%s is in set %s.", adt_ip, name); - res = 0; /* Return value for the program */ - } else { - ipset_printf("%s is NOT in set %s.", adt_ip, name); - res = 1; - } - - free(data); - return res; + "Tried to list sets from kernel %d times" + " and failed. Please try again when the load on" + " the sets has gone down.", LIST_TRIES); + else + kernel_error(CMD_LIST, errno); } /* Prints help @@ -1628,9 +1715,9 @@ set_testip(struct set *set, const char *name, const char *adt_ip) */ static void set_help(const struct settype *settype) { -#ifdef IP_SET_DEBUG +#ifdef IPSET_DEBUG char debughelp[] = - " --debug -z Enable debugging\n\n"; + " --debug -z Enable debugging\n\n"; #else char debughelp[] = "\n"; #endif @@ -1639,39 +1726,47 @@ static void set_help(const struct settype *settype) "Usage: %s -N new-set settype [options]\n" " %s -[XFLSH] [set] [options]\n" " %s -[EW] from-set to-set\n" - " %s -[ADT] set entry\n" + " %s -[ADTU] set IP\n" + " %s -B set IP option\n" " %s -R\n" " %s -h (print this help information)\n\n", - program_name, program_version, program_name, program_name, - program_name, program_name, program_name, program_name); + program_name, program_version, + program_name, program_name, program_name, + program_name, program_name, program_name, + program_name); printf("Commands:\n" "Either long or short options are allowed.\n" - " --create -N setname settype0[,settype1,...] \n" + " --create -N setname settype \n" " Create a new set\n" - " --create -N setname:IP[,IP...] settype \n" - " Create childset at setname:IP[,IP...]\n" - " --destroy -X [setname:IP,....]\n" - " Destroy a (child)set or all sets\n" - " --flush -F [setname:IP,...] [options]\n" - " Delete a (child)set or all sets\n" + " --destroy -X [setname]\n" + " Destroy a set or all sets\n" + " --flush -F [setname]\n" + " Flush a set or all sets\n" " --rename -E from-set to-set\n" " Rename from-set to to-set\n" " --swap -W from-set to-set\n" " Swap the content of two existing sets\n" - " --list -L [setname:IP,...] [options]\n" - " List the entries in a (child)set or all sets\n" + " --list -L [setname] [options]\n" + " List the IPs in a set or all sets\n" " --save -S [setname]\n" " Save the set or all sets to stdout\n" - " --restore -R\n" - " Restores from stdin a saved state\n" - " --add -A setname[:IP,...] entry[,entry...]\n" - " Add an entry to a (child)set\n" - " --del -D setname[:IP,...] entry[,entry...]\n" - " Deletes an entry from a (child)set\n" - " --test -T setname[:IP,...] entry[,entry...]\n" - " Tests if an entry exists in a (child)set.\n" - " --help -H [settype] [options]]\n" + " --restore -R [option]\n" + " Restores a saved state\n" + " --add -A setname IP\n" + " Add an IP to a set\n" + " --del -D setname IP\n" + " Deletes an IP from a set\n" + " --test -T setname IP \n" + " Tests if an IP exists in a set.\n" + " --bind -B setname IP|:default: -b bind-setname\n" + " Bind the IP in setname to bind-setname.\n" + " --unbind -U setname IP|:all:|:default:\n" + " Delete binding belonging to IP,\n" + " all bindings or default binding of setname.\n" + " --unbind -U :all: :all:|:default:\n" + " Delete all bindings or all default bindings.\n" + " --help -H [settype]\n" " Prints this help, and settype specific help\n" " --version -V\n" " Prints version information\n\n" @@ -1679,8 +1774,7 @@ static void set_help(const struct settype *settype) " --sorted -s Numeric sort of the IPs in -L\n" " --numeric -n Numeric output of addresses in a -L\n" " --quiet -q Suppress any output to stdout and stderr.\n" - " --childsets -c Operation valid for child sets\n" - " --hint -i Hint best settype initialization parameters\n"); + " --binding -b Specifies the binding for -B\n"); printf(debughelp); if (settype != NULL) { @@ -1689,470 +1783,222 @@ static void set_help(const struct settype *settype) } } -/* Hint various infos on a given settype */ -static int -settype_hint(const struct settype *settype, unsigned options) +static int find_cmd(const char option) { - char buffer[1024]; - ip_set_ip_t ip[MAX_HINT_SIZE]; - ip_set_ip_t id = 0; - - if (!settype->hint) - return 0; - - while (fgets(buffer, sizeof(buffer), stdin) != NULL - && id < MAX_HINT_SIZE) { - if (buffer[0] == '\n' || buffer[0] == '#') - continue; - switch (settype->typecode) { - case IPSET_TYPE_IP: - parse_ip(buffer, &ip[id++]); - break; - case IPSET_TYPE_PORT: - parse_port(buffer, &ip[id++]); - break; - default: - ; - } - } - if (id >= MAX_HINT_SIZE) - exit_error(OTHER_PROBLEM, - "More than %ld entries, exiting.", - MAX_HINT_SIZE); - if (id < 1) - exit_error(OTHER_PROBLEM, - "No input, no hint."); - - settype->hint(settype->data, ip, id); + int i; - return 0; + for (i = 1; i <= NUMBER_OF_CMD; i++) + if (cmdflags[i] == option) + return i; + + return CMD_NONE; } -static void init_sets(void) +static int parse_adt_cmdline(unsigned command, + const char *name, + char *adt, + struct set **set, + struct settype **settype) { - int version; - static int done = 0; - - if (done) - return; - - version = get_protocolversion(); - if (version != IP_SET_PROTOCOL_VERSION) - exit_error(OTHER_PROBLEM, - "Kernel ipset code is of protocol version %u." - "I'm of protocol version %u.\n" - "Please upgrade your kernel and/or ipset(8) utillity.", - version, IP_SET_PROTOCOL_VERSION); + int res = 0; - /* Get the list of existing sets from the kernel */ - get_sets(); - done = 1; + /* -U :all: :all:|:default: */ + if (command == CMD_UNBIND) { + if (strcmp(name, IPSET_TOKEN_ALL) == 0) { + if (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0 + || strcmp(adt, IPSET_TOKEN_ALL) == 0) { + *set = NULL; + *settype = NULL; + return 1; + } else + exit_error(PARAMETER_PROBLEM, + "-U %s requires %s or %s as binding name", + IPSET_TOKEN_ALL, + IPSET_TOKEN_DEFAULT, + IPSET_TOKEN_ALL); + } + } + *set = restore ? set_find_byname(name) + : set_adt_get(name); + + /* Reset space for adt data */ + *settype = (*set)->settype; + memset((*settype)->data, 0, (*settype)->adt_size); + + if ((command == CMD_TEST + || command == CMD_BIND + || command == CMD_UNBIND) + && (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0 + || strcmp(adt, IPSET_TOKEN_ALL) == 0)) + res = 1; + else + res = (*settype)->adt_parser( + command, + adt, + (*settype)->data); + + return res; } /* Main worker function */ -int parse_commandline(int argc, char *argv[], int exec_restore) +int parse_commandline(int argc, char *argv[]) { int res = 0; - unsigned command = 0; + unsigned command = CMD_NONE; unsigned options = 0; int c; - - struct set *set = NULL; - struct set *set_to = NULL; /* Used by -W */ - struct settype *settype = NULL; /* Used by -H */ - char *name = NULL; /* Used by -E */ - char *entries = NULL; /* Used by -A, -D, -T */ + + FILE *in = stdin; /* -R */ + + char *name = NULL; /* All except -H, -R */ + char *newname = NULL; /* -E, -W */ + char *adt = NULL; /* -A, -D, -T, -B, -U */ + char *binding = NULL; /* -B */ + struct set *set = NULL; /* -A, -D, -T, -B, -U */ + struct settype *settype = NULL; /* -N, -H */ + char all_sets[] = IPSET_TOKEN_ALL; struct option *opts = opts_long; /* Suppress error messages: we may add new options if we demand-load a protocol. */ opterr = 0; - + /* Reset optind to 0 for restore */ + optind = 0; + while ((c = getopt_long(argc, argv, opts_short, opts, NULL)) != -1) { DP("commandline parsed: opt %c (%s)", c, argv[optind]); switch (c) { /* - * Command selection. + * Command selection */ case 'h': - case 'H':{ /* Help */ + case 'H':{ /* Help: -H [typename [options]] */ + check_protocolversion(); set_command(&command, CMD_HELP); if (optarg) - set_checktype(optarg); + settype = check_set_typename(optarg); else if (optind < argc && argv[optind][0] != '-') - set_checktype(argv[optind++]); - - if (!set_typename_level) - break; - - if (set_typename_level != 1) - exit_error(PARAMETER_PROBLEM, - "-%c requires one settype as argument", - cmd2char(CMD_HELP)); - - settype = settype_load(set_typename[0]); - - /* Merge the hint options */ - if (settype->hint_parse) { - opts = merge_options(opts, - settype->hint_opts, - &settype->option_offset); - - /* Reset space for settype create data */ - memset(settype->data, 0, settype->hint_size); - - /* Zero the flags */ - settype->flags = 0; - - DP("call hint_init"); - /* Call the settype hint_init */ - settype->hint_init(settype->data); - } + settype = check_set_typename(argv[optind++]); break; } case 'V':{ /* Version */ - /* Dont display kernel protocol version because - * that might generate errors if the ipset module - * is not loaded in.*/ printf("%s v%s Protocol version %u.\n", program_name, program_version, IP_SET_PROTOCOL_VERSION); + check_protocolversion(); exit(0); } - - case 'N':{ /* Create */ - char *typename = NULL; - int i; - - init_sets(); - - DP("check setname"); - /* setname */ - set = set_checkname(optarg); - - if (set_level == 0) { - /* New set to be created */ - DP(" new set, set_level == 0"); + case 'N':{ /* Create: -N name typename options */ + set_command(&command, CMD_CREATE); - if (set != NULL) - exit_error(OTHER_PROBLEM, - "Set %s already exists.", - set_name); - set = ipset_malloc(sizeof(struct set)); - memset(set, 0, sizeof(struct set)); - - set_command(&command, CMD_CREATE); - - /* typename */ - if (optind < argc - && argv[optind][0] != '-') - typename = argv[optind++]; - else - exit_error(PARAMETER_PROBLEM, - "-%c requires new-setname and settype", - cmd2char(CMD_CREATE)); - - DP(" check typename"); - set_checktype(typename); - - strcpy(set->name, set_name); - set->levels = set_typename_level; - - DP("load the settypes"); - for (i = 0; i < set_typename_level; i++) - set->settype[i] = settype_load(set_typename[i]); - - } else {/* set_level != 0 */ - /* New childset to be created */ - - DP(" new childset, set_level != 0"); - - if (set == NULL) - exit_error(OTHER_PROBLEM, - "Set %s does not exist.", - set_name); - set_command(&command, CMD_CREATE_CHILD); - - /* typename */ - if (optind < argc - && argv[optind][0] != '-') - typename = argv[optind++]; - else - exit_error(PARAMETER_PROBLEM, - "-%c requires new-setname and settype", - cmd2char(CMD_CREATE)); - - DP(" check typename"); - set_checktype(typename); - if (set_typename_level > 1) - exit_error(PARAMETER_PROBLEM, - "-%c requires single settype specified", - cmd2char(CMD_CREATE)); - else if (set_level >= set->levels) - exit_error(PARAMETER_PROBLEM, - "specified childset is deeper than " - "%s set itself.", set->name); - else if (strcmp(typename, set->settype[set_level]->typename)) - exit_error(PARAMETER_PROBLEM, - "settype '%s' must be used instead of " - "'%s' in childset '%s'", - set->settype[set_level]->typename, - typename, optarg); - } + name = check_set_name(optarg); + + /* Protect reserved names (binding) */ + if (name[0] == ':') + exit_error(PARAMETER_PROBLEM, + "setname might not start with colon", + cmd2char(CMD_CREATE)); + + if (optind < argc + && argv[optind][0] != '-') + settype = check_set_typename(argv[optind++]); + else + exit_error(PARAMETER_PROBLEM, + "-%c requires setname and settype", + cmd2char(CMD_CREATE)); DP("merge options"); /* Merge the create options */ opts = merge_options(opts, - set->settype[set_level]->create_opts, - &set->settype[set_level]->option_offset); + settype->create_opts, + &settype->option_offset); - /* Reset space for settype create data */ - memset(set->settype[set_level]->data, 0, - set->settype[set_level]->create_size); + /* Reset space for create data */ + memset(settype->data, 0, settype->create_size); /* Zero the flags */ - set->settype[set_level]->flags = 0; + settype->flags = 0; DP("call create_init"); /* Call the settype create_init */ - set->settype[set_level]->create_init( - set->settype[set_level]->data); + settype->create_init(settype->data); break; } - case 'X':{ /* Destroy */ - init_sets(); - - set_command(&command, CMD_DESTROY); + case 'X': /* Destroy */ + case 'F': /* Flush */ + case 'L': /* List */ + case 'S':{ /* Save */ + set_command(&command, find_cmd(c)); if (optarg) - set = set_find_byname(optarg); + name = check_set_name(optarg); else if (optind < argc && argv[optind][0] != '-') - set = set_find_byname(argv[optind++]); + name = check_set_name(argv[optind++]); else - set = NULL; /* Mark to destroy all (empty) sets */ + name = all_sets; - if (set && set_level >= set->levels) - exit_error(PARAMETER_PROBLEM, - "specified childset is deeper than " - "%s set itself.", set->name); break; } - case 'F':{ /* Flush */ - init_sets(); - - set_command(&command, CMD_FLUSH); - - DP("flush: %s", optarg); - - if (optarg) - set = set_find_byname(optarg); - else if (optind < argc - && argv[optind][0] != '-') - set = set_find_byname(argv[optind++]); - else - set = NULL; /* Mark to flush all */ + case 'R':{ /* Restore */ + set_command(&command, find_cmd(c)); - if (set && set_level >= set->levels) - exit_error(PARAMETER_PROBLEM, - "specified childset is deeper than " - "%s set itself.", set->name); break; } - case 'E':{ /* Rename */ - init_sets(); - - set_command(&command, CMD_RENAME); - - set = set_find_byname(optarg); - if (set_level) - exit_error(PARAMETER_PROBLEM, - "childsets cannot be swapped"); + case 'E': /* Rename */ + case 'W':{ /* Swap */ + set_command(&command, find_cmd(c)); + name = check_set_name(optarg); if (optind < argc && argv[optind][0] != '-') - name = argv[optind++]; + newname = check_set_name(argv[optind++]); else exit_error(PARAMETER_PROBLEM, "-%c requires a setname " "and the new name for that set", cmd2char(CMD_RENAME)); - if (strlen(name) > IP_SET_MAXNAMELEN - 1) - exit_error(PARAMETER_PROBLEM, - "Setname '%s' is too long. Max %d characters.", - name, IP_SET_MAXNAMELEN - 1); - - /* Set with new name must not exist. */ - set_to = set_checkname(name); - if (set_to) - exit_error(PARAMETER_PROBLEM, - "Set already exists, cannot rename to %s", - name); - if (set_level) - exit_error(PARAMETER_PROBLEM, - "childsets cannot be swapped"); - - break; - } - - case 'W':{ /* Swap */ - char *name = NULL; - - init_sets(); - - set_command(&command, CMD_SWAP); - - set = set_find_byname(optarg); - if (set_level) - exit_error(PARAMETER_PROBLEM, - "childsets cannot be swapped"); - - if (optind < argc - && argv[optind][0] != '-') - name = argv[optind++]; - else - exit_error(PARAMETER_PROBLEM, - "-%c requires the names of two " - "existing sets", - cmd2char(CMD_SWAP)); - if (strlen(name) > IP_SET_MAXNAMELEN - 1) - exit_error(PARAMETER_PROBLEM, - "Setname '%s' is too long. Max %d characters.", - name, IP_SET_MAXNAMELEN - 1); - - /* Both sets must exist. */ - set_to = set_find_byname(name); - if (set_level) - exit_error(PARAMETER_PROBLEM, - "childsets cannot be swapped"); - - break; - } - - case 'L':{ /* List */ - init_sets(); - - set_command(&command, CMD_LIST); - if (optarg) - set = set_find_byname(optarg); - else if (optind < argc - && argv[optind][0] != '-') - set = set_find_byname(argv[optind++]); - else - set = NULL; /* Mark all */ - - if (set && set_level >= set->levels) - exit_error(PARAMETER_PROBLEM, - "specified childset is deeper than " - "%s set itself.", set->name); - break; - } - - case 'S':{ /* Save */ - init_sets(); - - set_command(&command, CMD_SAVE); - if (optarg) - set = set_find_byname(optarg); - else if (optind < argc - && argv[optind][0] != '-') - set = set_find_byname(argv[optind++]); - else - set = NULL; /* Mark to save all */ - - if (set && set_level >= set->levels) - exit_error(PARAMETER_PROBLEM, - "specified childset is deeper than " - "%s set itself.", set->name); - break; - } - - case 'R':{ /* Restore */ - init_sets(); - - set_command(&command, CMD_RESTORE); - break; - } - - case 'A':{ /* Add IP */ - init_sets(); - - set_command(&command, CMD_ADD); - - set = set_find_byname(optarg); - - if (set_level >= set->levels) - exit_error(PARAMETER_PROBLEM, - "specified childset is deeper than " - "%s set itself.", set->name); - - /* entries */ - if (optind < argc - && argv[optind][0] != '-') - entries = argv[optind++]; - else - exit_error(PARAMETER_PROBLEM, - "-%c requires setname and entries", - cmd2char(CMD_ADD)); break; } - case 'D':{ /* Del IP */ - init_sets(); - - set_command(&command, CMD_DEL); + case 'A': /* Add IP */ + case 'D': /* Del IP */ + case 'T': /* Test IP */ + case 'B': /* Bind IP */ + case 'U':{ /* Unbind IP */ + set_command(&command, find_cmd(c)); - set = set_find_byname(optarg); - - if (set_level >= set->levels) - exit_error(PARAMETER_PROBLEM, - "specified childset is deeper than " - "%s set itself.", set->name); + name = check_set_name(optarg); + /* IP */ if (optind < argc && argv[optind][0] != '-') - entries = argv[optind++]; + adt = argv[optind++]; else exit_error(PARAMETER_PROBLEM, - "-%c requires setname and entries", - cmd2char(CMD_DEL)); + "-%c requires setname and IP", + c); - break; - } + res = parse_adt_cmdline(command, name, adt, + &set, &settype); - case 'T':{ /* Test IP */ - init_sets(); - - set_command(&command, CMD_TEST); - - set = set_find_byname(optarg); - name = optarg; - - if (set_level >= set->levels) - exit_error(PARAMETER_PROBLEM, - "specified childset is deeper than " - "%s set itself.", set->name); - - if (optind < argc - && argv[optind][0] != '-') - entries = argv[optind++]; - else + if (!res) exit_error(PARAMETER_PROBLEM, - "-%c requires setname and entries", - cmd2char(CMD_TEST)); + "Unknown arg `%s'", + argv[optind - 1]); break; } @@ -2172,19 +2018,16 @@ int parse_commandline(int argc, char *argv[], int exec_restore) option_quiet = 1; break; -#ifdef IP_SET_DEBUG +#ifdef IPSET_DEBUG case 'z': /* debug */ add_option(&options, OPT_DEBUG); option_debug = 1; break; #endif - case 'c': - add_option(&options, OPT_CHILDSETS); - break; - - case 'i': - add_option(&options, OPT_HINT); + case 'b': + add_option(&options, OPT_BINDING); + binding = check_set_name(optarg); break; case 1: /* non option */ @@ -2197,31 +2040,13 @@ int parse_commandline(int argc, char *argv[], int exec_restore) switch (command) { case CMD_CREATE: - res = set->settype[set_level]->create_parse( - c - set->settype[set_level]->option_offset, - argv, - set->settype[set_level]->data, - &set->settype[set_level]->flags); - break; - - case CMD_CREATE_CHILD: - res = set->settype[set_level]->create_parse( - c - set->settype[set_level]->option_offset, - argv, - set->settype[set_level]->data, - &set->settype[set_level]->flags); - break; - - case CMD_HELP: { - if (!(settype && settype->hint_parse)) - break; - - res = settype->hint_parse( + res = settype->create_parse( c - settype->option_offset, argv, - settype->data); + settype->data, + &settype->flags); break; - } + default: res = 0; /* failed */ } /* switch (command) */ @@ -2243,75 +2068,90 @@ int parse_commandline(int argc, char *argv[], int exec_restore) if (optind < argc) exit_error(PARAMETER_PROBLEM, "unknown arguments found on commandline"); - if (!command) + if (command == CMD_NONE) exit_error(PARAMETER_PROBLEM, "no command specified"); /* Check options */ - generic_opt_check(command == CMD_CREATE_CHILD ? CMD_CREATE : command, options); + generic_opt_check(command, options); DP("cmd: %c", cmd2char(command)); switch (command) { case CMD_CREATE: DP("CMD_CREATE"); - set_create(set); - break; - - case CMD_CREATE_CHILD: - DP("CMD_CREATE_CHILD"); - set_create_childset(set, options); + if (restore) + set_restore_create(name, settype); + else + set_create(name, settype); break; case CMD_DESTROY: - set_destroy(set); + set_destroy(name, IP_SET_OP_DESTROY, CMD_DESTROY); break; case CMD_FLUSH: - set_flush(set, options); + set_destroy(name, IP_SET_OP_FLUSH, CMD_FLUSH); break; case CMD_RENAME: - set_rename(set, name); + set_rename(name, newname, IP_SET_OP_RENAME, CMD_RENAME); break; case CMD_SWAP: - set_swap(set, set_to); + set_rename(name, newname, IP_SET_OP_SWAP, CMD_SWAP); break; case CMD_LIST: - set_list(set, options); + list_sets(name, options); break; case CMD_SAVE: - set_save(set); + set_save(name); break; case CMD_RESTORE: - set_restore(); + set_restore(in, argv[0]); break; case CMD_ADD: - set_addip(set, entries); + if (restore) + set_restore_add(set, adt); + else + set_adtip(set, adt, IP_SET_OP_ADD_IP, CMD_ADD); break; case CMD_DEL: - set_delip(set, entries, options); + set_adtip(set, adt, IP_SET_OP_DEL_IP, CMD_DEL); break; case CMD_TEST: - res = set_testip(set, name, entries); + if (binding) + res = set_bind(set, adt, binding, + IP_SET_OP_TEST_BIND_SET, CMD_TEST); + else + res = set_adtip(set, adt, + IP_SET_OP_TEST_IP, CMD_TEST); break; - case CMD_HELP: - if (options & OPT_HINT) - res = settype_hint(settype, options); + case CMD_BIND: + if (restore) + set_restore_bind(set, adt, binding); else - set_help(settype); + set_bind(set, adt, binding, + IP_SET_OP_BIND_SET, CMD_BIND); + break; + + case CMD_UNBIND: + set_bind(set, adt, "", IP_SET_OP_UNBIND_SET, CMD_UNBIND); + break; + + case CMD_HELP: + set_help(settype); break; default: /* Will never happen */ - ; /* Keep the compiler happy */ + break; /* Keep the compiler happy */ } /* switch( command ) */ @@ -2320,7 +2160,7 @@ int parse_commandline(int argc, char *argv[], int exec_restore) int main(int argc, char *argv[]) -{ - return parse_commandline(argc, argv, 0); +{ + return parse_commandline(argc, argv); } diff --git a/ipset.h b/ipset.h index 095f756..e563331 100644 --- a/ipset.h +++ b/ipset.h @@ -26,20 +26,12 @@ #include -char program_name[] = "ipset"; -char program_version[] = "1.0"; - #define IPSET_LIB_NAME "/libipset_%s.so" #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" #define LIST_TRIES 5 -/* FIXME: move this to Makefile ? */ -#if 1 -#define IP_SET_DEBUG -#endif - -#ifdef IP_SET_DEBUG +#ifdef IPSET_DEBUG extern int option_debug; #define DP(format, args...) if (option_debug) \ do { \ @@ -50,34 +42,47 @@ extern int option_debug; #define DP(format, args...) #endif +/* Commands */ +enum set_commands { + CMD_NONE, + CMD_CREATE, /* -N */ + CMD_DESTROY, /* -X */ + CMD_FLUSH, /* -F */ + CMD_RENAME, /* -E */ + CMD_SWAP, /* -W */ + CMD_LIST, /* -L */ + CMD_SAVE, /* -S */ + CMD_RESTORE, /* -R */ + CMD_ADD, /* -A */ + CMD_DEL, /* -D */ + CMD_TEST, /* -T */ + CMD_BIND, /* -B */ + CMD_UNBIND, /* -U */ + CMD_HELP, /* -H */ + CMD_VERSION, /* -V */ + NUMBER_OF_CMD = CMD_VERSION, + /* Internal commands */ + CMD_MAX_SETS, + CMD_LIST_SIZE, + CMD_SAVE_SIZE, + CMD_ADT_GET, +}; + enum exittype { OTHER_PROBLEM = 1, PARAMETER_PROBLEM, VERSION_PROBLEM }; -struct set_data { - void *setdata; /* Hook to set speficic data */ - void *bitmap; /* Which elements has got a childset (bitmap) */ - void *adt; /* Add/del/test data */ -}; - -/* The simplistic view of an ipset */ +/* The view of an ipset in userspace */ struct set { - struct set *next; - char name[IP_SET_MAXNAMELEN]; /* Name of the set */ - unsigned id; /* Pool id in kernel */ - unsigned levels; /* Levels we may have in this set */ + ip_set_id_t id; /* Unique set id */ + ip_set_id_t index; /* Array index */ unsigned ref; /* References in kernel */ - struct settype *settype[IP_SET_LEVELS]; /* Pointer to set type functions */ - struct set_data private[IP_SET_LEVELS]; /* Hook to set specific data */ + struct settype *settype; /* Pointer to set type functions */ }; -#define ADT_ADD 0 -#define ADT_DEL 1 -#define ADT_TEST 2 - struct settype { struct settype *next; @@ -106,79 +111,49 @@ struct settype { /* Pointer to list of extra command-line options for create */ struct option *create_opts; - /* * Add/del/test IP */ /* Size of data. Will be sent to kernel */ - size_t req_size; + size_t adt_size; /* Function which parses command options */ - ip_set_ip_t (*adt_parser) (int cmd, const char *option, - void *data, const void * setdata); + ip_set_ip_t (*adt_parser) (unsigned cmd, const char *optarg, void *data); /* * Printing */ - /* Set up the sets headerinfo with data coming from kernel */ - void (*initheader) (void ** setdata, void *data, size_t len); - - /* Set up the sets members with data coming from kernel */ - void (*initmembers) (void * setdata, void *data, size_t len); + /* Size of header. */ + size_t header_size; - /* Remove the members memory usage */ - void (*killmembers) (void ** setdata); + /* Initialize the type-header */ + void (*initheader) (struct set *set, const void *data); /* Pretty print the type-header */ - void (*printheader) (const void * setdata, unsigned options); - - /* Returns an IP address from the set */ - ip_set_ip_t (*getipbyid) (const void * setdata, ip_set_ip_t id); - - /* Return the size of the set in ids */ - ip_set_ip_t (*sizeid) (const void * setdata); - + void (*printheader) (struct set *set, unsigned options); + /* Pretty print all IPs */ - void (*printips) (const void * setdata, unsigned options); + void (*printips) (struct set *set, void *data, size_t len, unsigned options); /* Pretty print all IPs sorted */ - void (*printips_sorted) (const void * setdata, - unsigned options); + void (*printips_sorted) (struct set *set, void *data, size_t len, unsigned options); /* Print save arguments for creating the set */ - void (*saveheader) (const void * setdata); + void (*saveheader) (struct set *set, unsigned options); /* Print save for all IPs */ - void (*saveips) (const void * setdata); + void (*saveips) (struct set *set, void *data, size_t len, unsigned options); /* Print usage */ void (*usage) (void); - /* - * Hint: size of data must be smaller than that of create! - */ - - /* Size of hint data. Will *not* be sent to kernel */ - size_t hint_size; - - /* Initialize the hint data. */ - void (*hint_init) (void *data); - - /* Function which parses command options; returns true if it ate an option */ - int (*hint_parse) (int c, char *argv[], void *data); - - /* Pointer to list of extra command-line options for hinting */ - struct option *hint_opts; - - /* Hint initialization info */ - void (*hint) (const void *data, ip_set_ip_t * ip, ip_set_ip_t id); - /* Internal data */ + void *header; + void *data; unsigned int option_offset; unsigned int flags; - void *data; }; extern void settype_register(struct settype *settype); diff --git a/ipset_iphash.c b/ipset_iphash.c index ea18296..8d6cda6 100644 --- a/ipset_iphash.c +++ b/ipset_iphash.c @@ -35,15 +35,10 @@ #define BUFLEN 30; -#define OPT_CREATE_INITVAL 0x01U -#define OPT_CREATE_HASHSIZE 0x02U -#define OPT_CREATE_NETMASK 0x03U - -#define OPT_HINT_TRY 0x01U -#define OPT_HINT_NETMASK 0x02U - -#define HINT_DEFAULT_TRY 8 -#define HINT_DEFAULT_FACTOR 4 +#define OPT_CREATE_HASHSIZE 0x01U +#define OPT_CREATE_PROBES 0x02U +#define OPT_CREATE_RESIZE 0x04U +#define OPT_CREATE_NETMASK 0x08U /* Initialize the create. */ void create_init(void *data) @@ -52,8 +47,12 @@ void create_init(void *data) (struct ip_set_req_iphash_create *) data; DP("create INIT"); + + /* Default create parameters */ + mydata->hashsize = 1024; + mydata->probes = 8; + mydata->resize = 50; - mydata->initval = 0; mydata->netmask = 0xFFFFFFFF; } @@ -62,36 +61,49 @@ int create_parse(int c, char *argv[], void *data, unsigned *flags) { struct ip_set_req_iphash_create *mydata = (struct ip_set_req_iphash_create *) data; - char *end; unsigned int bits; + ip_set_ip_t value; DP("create_parse"); switch (c) { case '1': - errno = 0; - mydata->initval = strtoul(optarg, &end, 0); - if (*end == '\0' && end != optarg && errno != ERANGE) { - *flags |= OPT_CREATE_INITVAL; + if (string_to_number(optarg, 1, UINT_MAX - 1, &mydata->hashsize)) + exit_error(PARAMETER_PROBLEM, "Invalid hashsize `%s' specified", optarg); + + *flags |= OPT_CREATE_HASHSIZE; + + DP("--hashsize %u", mydata->hashsize); + + break; - DP("--initval 0x%x)", mydata->initval); - break; - } - exit_error(PARAMETER_PROBLEM, "Invalid initval `%s' specified", optarg); case '2': - if (string_to_number(optarg, 1, MAX_RANGE, &mydata->hashsize)) - exit_error(PARAMETER_PROBLEM, "Hashsize `%s' specified", optarg); + if (string_to_number(optarg, 1, 65535, &value)) + exit_error(PARAMETER_PROBLEM, "Invalid probes `%s' specified", optarg); - *flags |= OPT_CREATE_HASHSIZE; + mydata->probes = value; + *flags |= OPT_CREATE_PROBES; - DP("--hashsize %u", mydata->hashsize); + DP("--probes %u", mydata->probes); break; case '3': + if (string_to_number(optarg, 0, 65535, &value)) + exit_error(PARAMETER_PROBLEM, "Invalid resize `%s' specified", optarg); + + mydata->resize = value; + *flags |= OPT_CREATE_RESIZE; + + DP("--resize %u", mydata->resize); + + break; + + case '4': + if (string_to_number(optarg, 0, 32, &bits)) exit_error(PARAMETER_PROBLEM, "Invalid netmask `%s' specified", optarg); @@ -118,119 +130,46 @@ void create_final(void *data, unsigned int flags) struct ip_set_req_iphash_create *mydata = (struct ip_set_req_iphash_create *) data; - if ((flags & OPT_CREATE_HASHSIZE) == 0) - exit_error(PARAMETER_PROBLEM, - "Need to specify --hashsize\n"); - - if (!mydata->initval) { - srand(getpid() | time(NULL)); - mydata->initval = rand(); - } - DP("initval: 0x%x hashsize %d", mydata->initval, mydata->hashsize); + DP("hashsize %u probes %u resize %u", + mydata->hashsize, mydata->probes, mydata->resize); } /* Create commandline options */ static struct option create_opts[] = { - {"initval", 1, 0, '1'}, - {"hashsize", 1, 0, '2'}, - {"netmask", 1, 0, '3'}, + {"hashsize", 1, 0, '1'}, + {"probes", 1, 0, '2'}, + {"resize", 1, 0, '3'}, + {"netmask", 1, 0, '4'}, {0} }; /* Add, del, test parser */ -ip_set_ip_t adt_parser(int cmd, const char *optarg, - void *data, const void *setdata) +ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data) { struct ip_set_req_iphash *mydata = (struct ip_set_req_iphash *) data; - mydata->flags = 0; - - if (*optarg == '+') { - if (cmd == ADT_ADD) { - mydata->flags |= IPSET_ADD_OVERWRITE; - optarg++; - } else - exit_error(PARAMETER_PROBLEM, - "The '!' overwrite flag can be used only " - "when adding an IP address to the set\n"); - } parse_ip(optarg, &mydata->ip); return mydata->ip; }; -ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id) -{ - struct ip_set_iphash *mysetdata = - (struct ip_set_iphash *) setdata; - - return mysetdata->members[id]; -} - -ip_set_ip_t sizeid(const void *setdata) -{ - struct ip_set_iphash *mysetdata = - (struct ip_set_iphash *) setdata; - - return (mysetdata->hashsize); -} +/* + * Print and save + */ -void initheader(void **setdata, void *data, size_t len) +void initheader(struct set *set, const void *data) { struct ip_set_req_iphash_create *header = (struct ip_set_req_iphash_create *) data; - - DP("iphash: initheader() 1"); - - if (len != sizeof(struct ip_set_req_iphash_create)) - exit_error(OTHER_PROBLEM, - "Iphash: incorrect size of header. " - "Got %d, wanted %d.", len, - sizeof(struct ip_set_req_iphash_create)); - - *setdata = ipset_malloc(sizeof(struct ip_set_iphash)); - - DP("iphash: initheader() 2"); - - ((struct ip_set_iphash *) *setdata)->initval = - header->initval; - ((struct ip_set_iphash *) *setdata)->hashsize = - header->hashsize; - ((struct ip_set_iphash *) *setdata)->netmask = - header->netmask; -} - -void initmembers(void *setdata, void *data, size_t len) -{ - struct ip_set_iphash *mysetdata = - (struct ip_set_iphash *) setdata; - size_t size; - - DP("iphash: initmembers()"); - - /* Check so we get the right amount of memberdata */ - size = sizeof(ip_set_ip_t) * mysetdata->hashsize; - - if (len != size) - exit_error(OTHER_PROBLEM, - "Iphash: incorrect size of members. " - "Got %d, wanted %d.", len, size); - - mysetdata->members = data; -} - -void killmembers(void **setdata) -{ - struct ip_set_iphash *mysetdata = - (struct ip_set_iphash *) *setdata; - - DP("iphash: killmembers()"); - - if (mysetdata->members != NULL) - free(mysetdata->members); - - ipset_free(setdata); + struct ip_set_iphash *map = + (struct ip_set_iphash *) set->settype->header; + + memset(map, 0, sizeof(struct ip_set_iphash)); + map->hashsize = header->hashsize; + map->probes = header->probes; + map->resize = header->resize; + map->netmask = header->netmask; } unsigned int @@ -249,188 +188,71 @@ mask_to_bits(ip_set_ip_t mask) return bits; } -void printheader(const void *setdata, unsigned options) +void printheader(struct set *set, unsigned options) { struct ip_set_iphash *mysetdata = - (struct ip_set_iphash *) setdata; + (struct ip_set_iphash *) set->settype->header; - printf(" initval: 0x%x", mysetdata->initval); - printf(" hashsize: %d", mysetdata->hashsize); + printf(" hashsize: %u", mysetdata->hashsize); + printf(" probes: %u", mysetdata->probes); + printf(" resize: %u", mysetdata->resize); if (mysetdata->netmask == 0xFFFFFFFF) printf("\n"); else printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask)); } -void printips(const void *setdata, unsigned options) +void printips(struct set *set, void *data, size_t len, unsigned options) { - struct ip_set_iphash *mysetdata = - (struct ip_set_iphash *) setdata; - ip_set_ip_t id; - - for (id = 0; id < mysetdata->hashsize; id++) - if (mysetdata->members[id]) - printf("%s\n", ip_tostring(mysetdata->members[id], options)); + size_t offset = 0; + ip_set_ip_t *ip; + + while (offset < len) { + ip = data + offset; + if (*ip) + printf("%s\n", ip_tostring(*ip, options)); + offset += sizeof(ip_set_ip_t); + } } -void saveheader(const void *setdata) +void saveheader(struct set *set, unsigned options) { - return; + struct ip_set_iphash *mysetdata = + (struct ip_set_iphash *) set->settype->header; + + printf("-N %s %s --hashsize %u --probes %u --resize %u", + set->name, set->settype->typename, + mysetdata->hashsize, mysetdata->probes, mysetdata->resize); + if (mysetdata->netmask == 0xFFFFFFFF) + printf("\n"); + else + printf(" --netmask %d\n", mask_to_bits(mysetdata->netmask)); } /* Print save for an IP */ -void saveips(const void *setdata) +void saveips(struct set *set, void *data, size_t len, unsigned options) { - return; + size_t offset = 0; + ip_set_ip_t *ip; + + while (offset < len) { + ip = data + offset; + if (*ip) + printf("-A %s %s\n", set->name, ip_tostring(*ip, options)); + offset += sizeof(ip_set_ip_t); + } } void usage(void) { printf - ("-N set iphash [--initval hash-initval] --hashsize hashsize [--netmask CIDR-netmask]\n" - "-A set [!]IP\n" + ("-N set iphash [--hashsize hashsize] [--probes probes ]\n" + " [--resize resize] [--netmask CIDR-netmask]\n" + "-A set IP\n" "-D set IP\n" - "-T set IP\n" - "-H iphash -i [--try number] [--factor number] [--netmask CIDR-netmask]\n"); -} - -struct ip_set_iphash_hint { - unsigned int try; - unsigned int factor; - ip_set_ip_t netmask; -}; - -/* Initialize the hint. */ -void hint_init(void *data) -{ - struct ip_set_iphash_hint *mydata = - (struct ip_set_iphash_hint *) data; - - DP("hint INIT"); - - mydata->try = HINT_DEFAULT_TRY; - mydata->factor = HINT_DEFAULT_FACTOR; - mydata->netmask = 0xFFFFFFFF; -} - -/* Function which parses command options; returns true if it ate an option */ -int hint_parse(int c, char *argv[], void *data) -{ - struct ip_set_iphash_hint *mydata = - (struct ip_set_iphash_hint *) data; - unsigned int bits; - - DP("hint_parse"); - - switch (c) { - case '1': - if (string_to_number(optarg, 1, 32, &mydata->try)) - exit_error(PARAMETER_PROBLEM, - "Invalid --try `%s' specified (out of range 1-32", optarg); - - DP("--try %i)", mydata->try); - break; - - case '2': - if (string_to_number(optarg, 1, 64, &mydata->factor)) - exit_error(PARAMETER_PROBLEM, - "Invalid --factor `%s' specified (out of range 1-64", optarg); - - DP("--factor %i)", mydata->factor); - break; - - case '3': - - if (string_to_number(optarg, 0, 32, &bits)) - exit_error(PARAMETER_PROBLEM, - "Invalid netmask `%s' specified", optarg); - - if (bits != 0) - mydata->netmask = 0xFFFFFFFF << (32 - bits); - - DP("--netmask %x", mydata->netmask); - - break; - - default: - return 0; - } - - return 1; -} - -/* Hint commandline options */ -static struct option hint_opts[] = { - {"try", 1, 0, '1'}, - {"factor", 1, 0, '2'}, - {"netmask", 1, 0, '3'}, - {0} -}; - -int hint_try(const ip_set_ip_t *ip, ip_set_ip_t *test, ip_set_ip_t best, - ip_set_ip_t hashsize, unsigned int try, uint32_t *initval, int *found) -{ - ip_set_ip_t id, hash; - int i = 0; - - next_try: - while (i < try) { - memset(test, 0, MAX_RANGE * sizeof(ip_set_ip_t)); - for (id = 0; id < best; id++) { - hash = jhash_1word(ip[id], *initval) % hashsize; - if (test[hash] != 0) { - *initval = rand(); - i++; - goto next_try; - } else - test[hash] = ip[id]; - DP("%i %x", hash, ip[id]); - } - printf("--initval 0x%08x --hashsize %d\n", *initval, hashsize); - *found = 1; - return 1; - } - return 0; + "-T set IP\n"); } -void hint(const void *data, ip_set_ip_t *ip, ip_set_ip_t best) -{ - struct ip_set_iphash_hint *mydata = - (struct ip_set_iphash_hint *) data; - uint32_t initval; - ip_set_ip_t next, prev, curr; - ip_set_ip_t test[MAX_RANGE]; - ip_set_ip_t id; - int found = 0; - - curr = MAX_RANGE > mydata->factor * best ? mydata->factor * best : MAX_RANGE; - prev = next = MAX_RANGE; - - srand(curr); - initval = rand(); - - for (id = 0; id < best; id++) - ip[id] &= mydata->netmask; - - while (1) { - DP("%u %u %u %u", prev, curr, next, best); - - if (hint_try(ip, test, best, curr, mydata->try, &initval, &found)) { - if (curr == best) - return; - next = (curr + best)/2; - } else { - if (curr == prev){ - if (!found) - printf("Cannot find good init values.\n"); - return; - } - next = (curr + prev)/2; - } - prev = curr; - curr = next; - } -} static struct settype settype_iphash = { .typename = SETTYPE_NAME, .typecode = IPSET_TYPE_IP, @@ -444,30 +266,18 @@ static struct settype settype_iphash = { .create_opts = create_opts, /* Add/del/test */ - .req_size = sizeof(struct ip_set_req_iphash), + .adt_size = sizeof(struct ip_set_req_iphash), .adt_parser = &adt_parser, - /* Get an IP address by id */ - .getipbyid = &getipbyid, - .sizeid = &sizeid, - /* Printing */ + .header_size = sizeof(struct ip_set_iphash), .initheader = &initheader, - .initmembers = &initmembers, - .killmembers = &killmembers, .printheader = &printheader, .printips = &printips, /* We only have the unsorted version */ .printips_sorted = &printips, .saveheader = &saveheader, .saveips = &saveips, .usage = &usage, - - /* Hint */ - .hint_size = sizeof(struct ip_set_iphash_hint), - .hint_init = &hint_init, - .hint_parse = &hint_parse, - .hint_opts = hint_opts, - .hint = &hint, }; void _init(void) diff --git a/ipset_ipmap.c b/ipset_ipmap.c index df03a14..7da8f1e 100644 --- a/ipset_ipmap.c +++ b/ipset_ipmap.c @@ -18,7 +18,7 @@ */ #include -#include +#include #include #include #include @@ -183,66 +183,31 @@ static struct option create_opts[] = { }; /* Add, del, test parser */ -ip_set_ip_t adt_parser(int cmd, const char *optarg, - void *data, const void *setdata) +ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data) { struct ip_set_req_ipmap *mydata = (struct ip_set_req_ipmap *) data; - struct ip_set_ipmap *mysetdata = - (struct ip_set_ipmap *) setdata; - DP("ipmap: %p %p", data, setdata); + DP("ipmap: %p %p", optarg, data); parse_ip(optarg, &mydata->ip); - - DP("from %s", ip_tostring(mysetdata->first_ip, 0)); - DP("to %s", ip_tostring(mysetdata->last_ip, 0)); - DP("ip %s", ip_tostring(mydata->ip, 0)); - - if (mydata->ip < mysetdata->first_ip || - mydata->ip > mysetdata->last_ip) - exit_error(PARAMETER_PROBLEM, "IP '%s' is out of range\n", - ip_tostring(mydata->ip, 0)); + DP("%s", ip_tostring(mydata->ip, 0)); return mydata->ip; } -ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id) -{ - struct ip_set_ipmap *mysetdata = - (struct ip_set_ipmap *) setdata; - - return (mysetdata->first_ip + id * mysetdata->hosts); -} - -ip_set_ip_t sizeid(const void *setdata) -{ - struct ip_set_ipmap *mysetdata = - (struct ip_set_ipmap *) setdata; - - return (mysetdata->sizeid); -} +/* + * Print and save + */ -void initheader(void **setdata, void *data, size_t len) +void initheader(struct set *set, const void *data) { struct ip_set_req_ipmap_create *header = (struct ip_set_req_ipmap_create *) data; - struct ip_set_ipmap *map; + struct ip_set_ipmap *map = + (struct ip_set_ipmap *) set->settype->header; - DP("ipmap: initheader() 1"); - - if (len != sizeof(struct ip_set_req_ipmap_create)) - exit_error(OTHER_PROBLEM, - "Ipmap: incorrect size of header. " - "Got %d, wanted %d.", len, - sizeof(struct ip_set_req_ipmap_create)); - - *setdata = ipset_malloc(sizeof(struct ip_set_ipmap)); - - DP("ipmap: initheader() 2"); - - map = (struct ip_set_ipmap *) *setdata; - + memset(map, 0, sizeof(struct ip_set_ipmap)); map->first_ip = header->from; map->last_ip = header->to; map->netmask = header->netmask; @@ -263,75 +228,66 @@ void initheader(void **setdata, void *data, size_t len) } DP("%i %i", map->hosts, map->sizeid ); - } -void initmembers(void *setdata, void *data, size_t len) +void printheader(struct set *set, unsigned options) { struct ip_set_ipmap *mysetdata = - (struct ip_set_ipmap *) setdata; - size_t size; - - DP("ipmap: initmembers()"); - - /* Check so we get the right amount of memberdata */ - size = bitmap_bytes(0, mysetdata->sizeid - 1); + (struct ip_set_ipmap *) set->settype->header; - if (len != size) - exit_error(OTHER_PROBLEM, - "Ipmap: incorrect size of members. " - "Got %d, wanted %d.", len, size); - - mysetdata->members = data; + printf(" from: %s", ip_tostring(mysetdata->first_ip, options)); + printf(" to: %s", ip_tostring(mysetdata->last_ip, options)); + if (mysetdata->netmask == 0xFFFFFFFF) + printf("\n"); + else + printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask)); } -void killmembers(void **setdata) +void printips_sorted(struct set *set, void *data, size_t len, unsigned options) { struct ip_set_ipmap *mysetdata = - (struct ip_set_ipmap *) *setdata; - - DP("ipmap: killmembers()"); + (struct ip_set_ipmap *) set->settype->header; + ip_set_ip_t id; - if (mysetdata->members != NULL) - ipset_free(&mysetdata->members); - - ipset_free(setdata); + for (id = 0; id < mysetdata->sizeid; id++) + if (test_bit(id, data)) + printf("%s\n", + ip_tostring(mysetdata->first_ip + + id * mysetdata->hosts, + options)); } -void printheader(const void *setdata, unsigned options) +void saveheader(struct set *set, unsigned options) { struct ip_set_ipmap *mysetdata = - (struct ip_set_ipmap *) setdata; + (struct ip_set_ipmap *) set->settype->header; - printf(" from: %s", ip_tostring(mysetdata->first_ip, options)); - printf(" to: %s", ip_tostring(mysetdata->last_ip, options)); + printf("-N %s %s --from %s", + set->name, set->settype->typename, + ip_tostring(mysetdata->first_ip, options)); + printf(" --to %s", + ip_tostring(mysetdata->last_ip, options)); if (mysetdata->netmask == 0xFFFFFFFF) printf("\n"); else - printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask)); + printf(" --netmask %d\n", + mask_to_bits(mysetdata->netmask)); } -void printips_sorted(const void *setdata, unsigned options) +void saveips(struct set *set, void *data, size_t len, unsigned options) { struct ip_set_ipmap *mysetdata = - (struct ip_set_ipmap *) setdata; + (struct ip_set_ipmap *) set->settype->header; ip_set_ip_t id; + DP("%s", set->name); for (id = 0; id < mysetdata->sizeid; id++) - if (test_bit(id, mysetdata->members)) - printf("%s\n", ip_tostring(mysetdata->first_ip + id * mysetdata->hosts, - options)); -} - -void saveheader(const void *setdata) -{ - return; -} - -/* Print save for an IP */ -void saveips(const void *setdata) -{ - return; + if (test_bit(id, data)) + printf("-A %s %s\n", + set->name, + ip_tostring(mysetdata->first_ip + + id * mysetdata->hosts, + options)); } void usage(void) @@ -357,24 +313,18 @@ static struct settype settype_ipmap = { .create_opts = create_opts, /* Add/del/test */ - .req_size = sizeof(struct ip_set_req_ipmap), + .adt_size = sizeof(struct ip_set_req_ipmap), .adt_parser = &adt_parser, - /* Get an IP address by id */ - .getipbyid = &getipbyid, - .sizeid = &sizeid, - /* Printing */ + .header_size = sizeof(struct ip_set_ipmap), .initheader = &initheader, - .initmembers = &initmembers, - .killmembers = &killmembers, .printheader = &printheader, .printips = &printips_sorted, /* We only have sorted version */ .printips_sorted = &printips_sorted, .saveheader = &saveheader, .saveips = &saveips, .usage = &usage, - .hint = NULL, }; void _init(void) diff --git a/ipset_macipmap.c b/ipset_macipmap.c index eff42ce..5d12af4 100644 --- a/ipset_macipmap.c +++ b/ipset_macipmap.c @@ -175,119 +175,47 @@ static void parse_mac(const char *mac, unsigned char *ethernet) } /* Add, del, test parser */ -ip_set_ip_t adt_parser(int cmd, const char *optarg, - void *data, const void *setdata) +ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data) { struct ip_set_req_macipmap *mydata = (struct ip_set_req_macipmap *) data; - struct ip_set_macipmap *mysetdata = - (struct ip_set_macipmap *) setdata; char *saved = strdup(optarg); char *ptr, *tmp = saved; - DP("macipmap: %p %p", data, setdata); + DP("macipmap: %p %p", optarg, data); ptr = strsep(&tmp, "%"); parse_ip(ptr, &mydata->ip); - if (cmd == ADT_ADD && !tmp) - exit_error(PARAMETER_PROBLEM, - "Need to specify ip[mac]\n"); - if (tmp) parse_mac(tmp, mydata->ethernet); else memset(mydata->ethernet, 0, ETH_ALEN); - DP("from %s", ip_tostring(mysetdata->first_ip, 0)); - DP("to %s", ip_tostring(mysetdata->last_ip, 0)); - DP("ip %s", ip_tostring(mydata->ip, 0)); - - if (mydata->ip < mysetdata->first_ip || - mydata->ip > mysetdata->last_ip) - exit_error(PARAMETER_PROBLEM, "IP '%s' is out of range\n", - ip_tostring(mydata->ip, 0)); - - free(saved); - return mydata->ip; } -ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id) -{ - struct ip_set_macipmap *mysetdata = - (struct ip_set_macipmap *) setdata; - - return (mysetdata->first_ip + id); -} - -ip_set_ip_t sizeid(const void *setdata) -{ - struct ip_set_macipmap *mysetdata = - (struct ip_set_macipmap *) setdata; - - return (mysetdata->last_ip - mysetdata->first_ip + 1); -} +/* + * Print and save + */ -void initheader(void **setdata, void *data, size_t len) +void initheader(struct set *set, const void *data) { struct ip_set_req_macipmap_create *header = (struct ip_set_req_macipmap_create *) data; + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->settype->header; - DP("macipmap: initheader() 1"); - - if (len != sizeof(struct ip_set_req_macipmap_create)) - exit_error(OTHER_PROBLEM, - "Macipmap: incorrect size of header. " - "Got %d, wanted %d.", len, - sizeof(struct ip_set_req_macipmap_create)); - - *setdata = ipset_malloc(sizeof(struct ip_set_macipmap)); - - DP("bitmap: initheader() 2"); - - ((struct ip_set_macipmap *) *setdata)->first_ip = header->from; - ((struct ip_set_macipmap *) *setdata)->last_ip = header->to; - ((struct ip_set_macipmap *) *setdata)->flags = header->flags; -} - -void initmembers(void *setdata, void *data, size_t len) -{ - struct ip_set_macipmap *mysetdata = - (struct ip_set_macipmap *) setdata; - size_t size; - - DP("macipmap: initmembers()"); - - /* Check so we get the right amount of memberdata */ - size = (mysetdata->last_ip - mysetdata->first_ip) - * sizeof(struct ip_set_macip); - - if (len != size) - exit_error(OTHER_PROBLEM, - "Macipmap: incorrect size of members. " - "Got %d, wanted %d.", len, size); - - mysetdata->members = data; + memset(map, 0, sizeof(struct ip_set_macipmap)); + map->first_ip = header->from; + map->last_ip = header->to; + map->flags = header->flags; } -void killmembers(void **setdata) +void printheader(struct set *set, unsigned options) { struct ip_set_macipmap *mysetdata = - (struct ip_set_macipmap *) *setdata; - - DP("macipmap: killmembers()"); - - if (mysetdata->members != NULL) - ipset_free(&mysetdata->members); - - ipset_free(setdata); -} - -void printheader(const void *setdata, unsigned options) -{ - struct ip_set_macipmap *mysetdata = - (struct ip_set_macipmap *) setdata; + (struct ip_set_macipmap *) set->settype->header; printf(" from: %s", ip_tostring(mysetdata->first_ip, options)); printf(" to: %s", ip_tostring(mysetdata->last_ip, options)); @@ -306,18 +234,18 @@ static void print_mac(unsigned char macaddress[ETH_ALEN]) printf(":%02X", macaddress[i]); } -void printips_sorted(const void *setdata, unsigned options) +void printips_sorted(struct set *set, void *data, size_t len, unsigned options) { struct ip_set_macipmap *mysetdata = - (struct ip_set_macipmap *) setdata; + (struct ip_set_macipmap *) set->settype->header; struct ip_set_macip *table = - (struct ip_set_macip *) mysetdata->members; + (struct ip_set_macip *) data; u_int32_t addr = mysetdata->first_ip; while (addr <= mysetdata->last_ip) { if (test_bit(IPSET_MACIP_ISSET, - &table[addr - mysetdata->first_ip].flags)) { - printf("%s%s", ip_tostring(addr, options), "%"); + (void *)&table[addr - mysetdata->first_ip].flags)) { + printf("%s%%", ip_tostring(addr, options)); print_mac(table[addr - mysetdata->first_ip]. ethernet); printf("\n"); @@ -326,15 +254,40 @@ void printips_sorted(const void *setdata, unsigned options) } } -void saveheader(const void *setdata) +void saveheader(struct set *set, unsigned options) { - return; + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) set->settype->header; + + printf("-N %s %s --from: %s", + set->name, set->settype->typename, + ip_tostring(mysetdata->first_ip, options)); + printf(" --to: %s", ip_tostring(mysetdata->last_ip, options)); + + if (mysetdata->flags & IPSET_MACIP_MATCHUNSET) + printf(" --matchunset"); + printf("\n"); } -/* Print save for an IP */ -void saveips(const void *setdata) +void saveips(struct set *set, void *data, size_t len, unsigned options) { - return; + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) set->settype->header; + struct ip_set_macip *table = + (struct ip_set_macip *) data; + u_int32_t addr = mysetdata->first_ip; + + while (addr <= mysetdata->last_ip) { + if (test_bit(IPSET_MACIP_ISSET, + (void *)&table[addr - mysetdata->first_ip].flags)) { + printf("-A %s %s%%", + set->name, ip_tostring(addr, options)); + print_mac(table[addr - mysetdata->first_ip]. + ethernet); + printf("\n"); + } + addr++; + } } void usage(void) @@ -360,24 +313,18 @@ static struct settype settype_macipmap = { .create_opts = create_opts, /* Add/del/test */ - .req_size = sizeof(struct ip_set_req_macipmap), + .adt_size = sizeof(struct ip_set_req_macipmap), .adt_parser = &adt_parser, - /* Get an IP address by id */ - .getipbyid = &getipbyid, - .sizeid = &sizeid, - /* Printing */ + .header_size = sizeof(struct ip_set_macipmap), .initheader = &initheader, - .initmembers = &initmembers, - .killmembers = &killmembers, .printheader = &printheader, .printips = &printips_sorted, /* We only have sorted version */ .printips_sorted = &printips_sorted, .saveheader = &saveheader, .saveips = &saveips, .usage = &usage, - .hint = NULL, }; void _init(void) diff --git a/ipset_portmap.c b/ipset_portmap.c index fd8746f..ba9e651 100644 --- a/ipset_portmap.c +++ b/ipset_portmap.c @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -114,134 +115,82 @@ static struct option create_opts[] = { }; /* Add, del, test parser */ -ip_set_ip_t adt_parser(int cmd, const char *optarg, - void *data, const void *setdata) +ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data) { struct ip_set_req_portmap *mydata = (struct ip_set_req_portmap *) data; - struct ip_set_portmap *mysetdata = - (struct ip_set_portmap *) setdata; - - DP("portmap: %p %p", data, setdata); parse_port(optarg, &mydata->port); - - DP("from %s", port_tostring(mysetdata->first_port, 0)); - DP("to %s", port_tostring(mysetdata->last_port, 0)); - DP("port %s", port_tostring(mydata->port, 0)); - - if (mydata->port < mysetdata->first_port || - mydata->port > mysetdata->last_port) - exit_error(PARAMETER_PROBLEM, "Port '%s' is out of range\n", - port_tostring(mydata->port, 0)); + DP("%s", port_tostring(mydata->port, 0)); return mydata->port; } -ip_set_ip_t getportbyid(const void *setdata, ip_set_ip_t id) -{ - struct ip_set_portmap *mysetdata = - (struct ip_set_portmap *) setdata; - - return (mysetdata->first_port + id); -} - -ip_set_ip_t sizeid(const void *setdata) -{ - struct ip_set_portmap *mysetdata = - (struct ip_set_portmap *) setdata; - - return (mysetdata->last_port - mysetdata->first_port + 1); -} +/* + * Print and save + */ -void initheader(void **setdata, void *data, size_t len) +void initheader(struct set *set, const void *data) { struct ip_set_req_portmap_create *header = (struct ip_set_req_portmap_create *) data; + struct ip_set_portmap *map = + (struct ip_set_portmap *) set->settype->header; - DP("portmap: initheader() 1"); - - if (len != sizeof(struct ip_set_req_portmap_create)) - exit_error(OTHER_PROBLEM, - "Portmap: incorrect size of header. " - "Got %d, wanted %d.", len, - sizeof(struct ip_set_req_portmap_create)); - - *setdata = ipset_malloc(sizeof(struct ip_set_portmap)); - - DP("portmap: initheader() 2"); - - ((struct ip_set_portmap *) *setdata)->first_port = - header->from; - ((struct ip_set_portmap *) *setdata)->last_port = header->to; - + memset(map, 0, sizeof(struct ip_set_portmap)); + map->first_port = header->from; + map->last_port = header->to; } -void initmembers(void *setdata, void *data, size_t len) +void printheader(struct set *set, unsigned options) { struct ip_set_portmap *mysetdata = - (struct ip_set_portmap *) setdata; - size_t size; - - DP("portmap: initmembers()"); - - /* Check so we get the right amount of memberdata */ - size = bitmap_bytes(mysetdata->first_port, mysetdata->last_port); - - if (len != size) - exit_error(OTHER_PROBLEM, - "Portmap: incorrect size of members. " - "Got %d, wanted %d.", len, size); - - mysetdata->members = data; -} - -void killmembers(void **setdata) -{ - struct ip_set_portmap *mysetdata = - (struct ip_set_portmap *) *setdata; - - DP("portmap: killmembers()"); - - if (mysetdata->members != NULL) - ipset_free(&mysetdata->members); - - ipset_free(setdata); -} - - -void printheader(const void *setdata, unsigned options) -{ - struct ip_set_portmap *mysetdata = - (struct ip_set_portmap *) setdata; + (struct ip_set_portmap *) set->settype->header; printf(" from: %s", port_tostring(mysetdata->first_port, options)); printf(" to: %s\n", port_tostring(mysetdata->last_port, options)); } -void printports_sorted(const void *setdata, unsigned options) +void printports_sorted(struct set *set, void *data, size_t len, unsigned options) { struct ip_set_portmap *mysetdata = - (struct ip_set_portmap *) setdata; - + (struct ip_set_portmap *) set->settype->header; u_int32_t addr = mysetdata->first_port; + DP("%u -- %u", mysetdata->first_port, mysetdata->last_port); while (addr <= mysetdata->last_port) { - if (test_bit(addr - mysetdata->first_port, mysetdata->members)) + if (test_bit(addr - mysetdata->first_port, data)) printf("%s\n", port_tostring(addr, options)); addr++; } } -void saveheader(const void *setdata) +void saveheader(struct set *set, unsigned options) { - return; + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) set->settype->header; + + printf("-N %s %s --from %s", + set->name, + set->settype->typename, + port_tostring(mysetdata->first_port, options)); + printf(" --to %s\n", + port_tostring(mysetdata->last_port, options)); } -/* Print save for an IP */ -void saveports(const void *setdata) +void saveports(struct set *set, void *data, size_t len, unsigned options) { - return; + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) set->settype->header; + u_int32_t addr = mysetdata->first_port; + + while (addr <= mysetdata->last_port) { + if (test_bit(addr - mysetdata->first_port, data)) + printf("-A %s %s\n", + set->name, + port_tostring(addr, options)); + addr++; + } } void usage(void) @@ -266,24 +215,18 @@ static struct settype settype_portmap = { .create_opts = create_opts, /* Add/del/test */ - .req_size = sizeof(struct ip_set_req_portmap), + .adt_size = sizeof(struct ip_set_req_portmap), .adt_parser = &adt_parser, - /* Get an IP address by id */ - .getipbyid = &getportbyid, - .sizeid = &sizeid, - /* Printing */ + .header_size = sizeof(struct ip_set_portmap), .initheader = &initheader, - .initmembers = &initmembers, - .killmembers = &killmembers, .printheader = &printheader, .printips = &printports_sorted, /* We only have sorted version */ .printips_sorted = &printports_sorted, .saveheader = &saveheader, .saveips = &saveports, .usage = &usage, - .hint = NULL, }; void _init(void) diff --git a/libipt_set.h b/libipt_set.h index 8180f7c..0d5ab5d 100644 --- a/libipt_set.h +++ b/libipt_set.h @@ -16,117 +16,30 @@ static int get_set_getsockopt(void *data, size_t * size) return getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); } -static void get_set_byname(const char *setname, struct ipt_set_info *info) -{ - struct ip_set_req_get req; - int size = sizeof(struct ip_set_req_get); - int res; - - req.op = IP_SET_OP_GETSET_BYNAME; - strncpy(req.name, setname, IP_SET_MAXNAMELEN); - req.name[IP_SET_MAXNAMELEN - 1] = '\0'; - res = get_set_getsockopt(&req, &size); - if (res != 0) - exit_error(OTHER_PROBLEM, - "Problem when communicating with ipset. errno=%d.\n", - errno); - if (size != sizeof(struct ip_set_req_get)) - exit_error(OTHER_PROBLEM, - "Incorrect return size from kernel during ipset lookup, " - "(want %d, got %d)\n", - sizeof(struct ip_set_req_get), size); - if (req.id < 0) - exit_error(PARAMETER_PROBLEM, - "Set %s doesn't exist.\n", setname); - - info->id = req.id; -} - -static void get_set_byid(char * setname, unsigned id) -{ - struct ip_set_req_get req; - int size = sizeof(struct ip_set_req_get); - int res; - - req.op = IP_SET_OP_GETSET_BYID; - req.id = id; - res = get_set_getsockopt(&req, &size); - if (res != 0) - exit_error(OTHER_PROBLEM, - "Problem when communicating with ipset. errno=%d.\n", - errno); - if (size != sizeof(struct ip_set_req_get)) - exit_error(OTHER_PROBLEM, - "Incorrect return size from kernel during ipset lookup, " - "(want %d, got %d)\n", - sizeof(struct ip_set_req_get), size); - if (req.id < 0) - exit_error(PARAMETER_PROBLEM, - "Set id %i in kernel doesn't exist.\n", id); - - strncpy(setname, req.name, IP_SET_MAXNAMELEN); -} - static void -parse_pool(const char *optarg, struct ipt_set_info *info) +parse_bindings(const char *optarg, struct ipt_set_info *info) { char *saved = strdup(optarg); char *ptr, *tmp = saved; - - ptr = strsep(&tmp, ":"); - get_set_byname(ptr, info); + int i = 0; - while (info->set_level < IP_SET_SETIP_LEVELS && tmp) { + while (i < IP_SET_MAX_BINDINGS && tmp != NULL) { ptr = strsep(&tmp, ","); if (strncmp(ptr, "src", 3) == 0) - info->flags[info->set_level++] |= IPSET_SRC; + info->flags[i++] |= IPSET_SRC; else if (strncmp(ptr, "dst", 3) == 0) - info->flags[info->set_level++] |= IPSET_DST; + info->flags[i++] |= IPSET_DST; else exit_error(PARAMETER_PROBLEM, "You must spefify (the comma separated list of) 'src' or 'dst'."); } - if (tmp || info->set_level >= IP_SET_SETIP_LEVELS) + if (tmp) exit_error(PARAMETER_PROBLEM, - "Defined childset level is deeper that %i.", - IP_SET_SETIP_LEVELS); + "Can't follow bindings deeper than %i.", + IP_SET_MAX_BINDINGS - 1); free(saved); } -static int -parse_ipflags(const char *optarg, struct ipt_set_info *info) -{ - char *saved = strdup(optarg); - char *ptr, *tmp = saved; - int overwrite = 0; - - info->ip_level = info->set_level; - - while (info->ip_level < IP_SET_LEVELS && tmp) { - if (*tmp == '+') { - info->flags[info->ip_level] |= IPSET_ADD_OVERWRITE; - tmp++; - overwrite++; - } - ptr = strsep(&tmp, ","); - if (strncmp(ptr, "src", 3) == 0) - info->flags[info->ip_level++] |= IPSET_SRC; - else if (strncmp(ptr, "dst", 3) == 0) - info->flags[info->ip_level++] |= IPSET_DST; - else - exit_error(PARAMETER_PROBLEM, - "You must spefify (the comma separated list of) 'src' or 'dst'."); - } - - if (tmp || info->ip_level >= IP_SET_LEVELS) - exit_error(PARAMETER_PROBLEM, - "Defined level is deeper that %i.", - IP_SET_LEVELS); - - free(saved); - return overwrite; -} - #endif /*_LIBIPT_SET_H*/ -- cgit v1.2.3