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