diff options
author | kadlec <kadlec> | 2004-02-09 13:47:01 +0000 |
---|---|---|
committer | kadlec <kadlec> | 2004-02-09 13:47:01 +0000 |
commit | 2c3e49def40b41677182fc9243128f11c5e148e9 (patch) | |
tree | 53574085f70bc86589c22ea7cff030b03486e596 |
Userspace part of sets: ipset added (JK)
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | ChangeLog.ippool | 59 | ||||
-rw-r--r-- | Makefile | 48 | ||||
-rwxr-xr-x | ipset | bin | 0 -> 97124 bytes | |||
-rw-r--r-- | ipset.8 | 289 | ||||
-rw-r--r-- | ipset.c | 2326 | ||||
-rw-r--r-- | ipset.h | 203 | ||||
-rw-r--r-- | ipset_iphash.c | 477 | ||||
-rw-r--r-- | ipset_ipmap.c | 384 | ||||
-rw-r--r-- | ipset_macipmap.c | 387 | ||||
-rw-r--r-- | ipset_portmap.c | 293 | ||||
-rw-r--r-- | libipt_set.h | 132 |
12 files changed, 4604 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..c6e20fa --- /dev/null +++ b/ChangeLog @@ -0,0 +1,6 @@ +1.0 + - Renamed to ipset (Jozsef Kadlecsik) + - Rewritten to support child pools (Jozsef Kadlecsik) + - portmap, iphash pool support added (Jozsef Kadlecsik) + - too much other mods here and there to list... (Jozsef Kadlecsik) + diff --git a/ChangeLog.ippool b/ChangeLog.ippool new file mode 100644 index 0000000..669c304 --- /dev/null +++ b/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/Makefile b/Makefile new file mode 100644 index 0000000..23302b0 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +#!/usr/bin/make + +IPSET_VERSION:=v1.0 +IPSET_LIB_DIR:=$(DESTDIR)$(LIBDIR)/ipset +#IPSET_LIB_DIR:=. +#CFLAGS:=-I$(KERNEL_DIR)/include + +SETTYPES:=ipmap portmap macipmap iphash + +EXTRAS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_set.h ] && echo ipset/ipset) +EXTRAS+=$(foreach T, $(SETTYPES),ipset/libipset_$(T).so) +EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ipset $(DESTDIR)$(MANDIR)/man8/ipset.8 +EXTRA_INSTALLS+=$(foreach T, $(SETTYPES), $(DESTDIR)$(LIBDIR)/ipset/libipset_$(T).so) + +ifndef TOPLEVEL_INCLUDED +local: + cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS) + +else +EXTRA_DEPENDS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_set.h ] && echo "") +CFLAGS+=-DIPSET_VERSION=$(IPSET_VERSION) -DIPSET_LIB_DIR=\"$(IPSET_LIB_DIR)\" + +#The ipset(8) self +ipset/ipset.o: ipset/ipset.c + $(CC) $(CFLAGS) -g -c -o $@ $< + +ipset/ipset: ipset/ipset.o + $(CC) $(CFLAGS) -ldl -rdynamic -o $@ $^ + +#Pooltypes +ipset/ipset_%.o: ipset/ipset_%.c + $(CC) $(CFLAGS) -c -o $@ $< + +ipset/libipset_%.so: ipset/ipset_%.o + $(LD) -shared -o $@ $< + +$(DESTDIR)$(LIBDIR)/ipset/libipset_%.so: ipset/libipset_%.so + @[ -d $(DESTDIR)$(LIBDIR)/ipset ] || mkdir -p $(DESTDIR)$(LIBDIR)/ipset + cp $< $@ + +$(DESTDIR)$(BINDIR)/ipset: ipset/ipset + @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + cp $< $@ + +$(DESTDIR)$(MANDIR)/man8/ipset.8: ipset/ipset.8 + @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8 + cp $< $@ +endif Binary files differ@@ -0,0 +1,289 @@ +.TH IPSET 8 "Feb 05, 2004" "" "" +.\" +.\" Man page written by Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" +.SH NAME +ipset \- IP set administration +.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 -[ADT] " "set entry" +.br +.BR "ipset -R " +.SH DESCRIPTION +.B Ipset +is used to set up, maintain, and inspect so called IP sets in the Linux +kernel. Each set can contain a fixed number of entries and at every entry +there might be child sets, up to a given level. Child sets at the same +level must have the same type. +.P +In spite it is called IP sets, depending on the type the sets may +store not only IP addresses but (TCP/UDP) port numbers as well, or +additional informations besides IP addresses: the word IP means a general +term here. See the set type definitions below. +.P +Child sets are identified +by `IP', without the possible additional informations stored in the set. +There is no other relationship between the set entry and the child set +at that entry: the set entry can be added, deleted without disturbing +the child set and vice versa. Therefore below we denote pure IPs +identifying a child set by `IP', while entries in a set by `entry'. +.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 type0[,type1...] type0-options" +Create a set identified with setname, with listed types. +Type0-specific options must be supplied. When more than one set +type is given on the command line, room for the first level child +sets are automatically reserved. +.TP +.BI "-N, --create " "\fIsetname:IP[,...]\fP type type-options" +Create a child set at setname:IP[,...] identified with type, +which must correspond to the set type used when the set was created. +The parent set and parent child sets must exist. When the +.B "-c, --childsets" +option is used, space for child sets will be reserved. +.TP +.BI "-X, --destroy " "[\fIsetname[:IP,...]\fP]" +Destroy the specified set or child set, or all sets if none is +given. +.TP +.BI "-F, --flush " "[\fIsetname[:IP,...]\fP]" +Delete all entries from the specified set or child set, or from +all sets if none is given. When the +.B "-c, --childsets" +option is specified, child sets are recursively flushed too. +.TP +.BI "-E, --rename " "\fIfrom-setname\fP \fIto-setname\fP" +Rename a set. Set identified by to-setname must not exist. +.TP +.BI "-W, --swap " "\fIfrom-setname\fP \fIto-setname\fP" +Swap the content of two sets. Both sets must exist. +.TP +.BI "-L, --list " "[\fIsetname[:IP,...]\fP]" +List the entries from the specified set or child set, or from +all sets if none is given. When the +.B "-c, --childsets" +option is specified, child sets are recursively listed too. +The +.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. +.TP +.BI "-S, --save " "[\fIsetname[:IP,...]\fP]" +Save the set or child set, or all sets if none specified to stdout +in a format, that --restore can read. +.TP +.BI "-R, --restore " +Restore from stdin a saved session generated by --save. +.TP +.BI "-A, --add " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP" +Add an entry to a (child) set or multiple entries to the child sets +defined by the entries. In another words, the command +.nf + ipset -A set entry1,entry2 +.fi +is equivalent to +.nf + ipset -A set entry1 + ipset -A set:entry1-IP entry2 +.fi +.TP +.BI "-D, --del " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP" +Delete an entry from a (child) set or multiple entries from the child sets +defined by the entries. +.P +When multiple entries are added or deleted and the command fails at +a deeper level, the successfully added/deleted entries are not restored +to their previous value. +.TP +.BI "-T, --test " "\fIsetname[:IP,...]\fP \fIentry[:entry,...]\fP" +Test if an entry or multiple entries exist in a (child) set. Exit status +number is nonzero if any tested entry is missing from the set and +zero if they are all exists. +.TP +.BI "-H, --help " "[settype] [options]" +Print help and settype specific help. Some set types has additional +help options, see below. +.SS "OTHER OPTIONS" +The following additional options can be specified: +.TP +.B "-s, --sorted" +Sorted output. When listing sets, entries are listed sorted. +.TP +.B "-n, --numeric" +Numeric output. When listing sets, IP addresses and port numbers +will be printed in numeric format. By default the program will +try to display them as host names, network names, or services +(whenever applicable). +.TP +.B "-q, --quiet" +Suppress any output to stdout and stderr. Ipset will still return +possible errors. +.TP +.B "-c, --childsets" +Space will be reserved for child sets when creating a set, or list/flush +operation valid for child sets too. +.TP +.B "-i, --hint" +Hint best settype initialization parameters (for hash type sets). +.SH SET TYPES +ipset supports the following set types: +.SS ipmap +The ipmap set type uses a memory range, where each bit represents +one IP address. An ipmap set can store up to 65535 (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 65535 (B-class network) IP addresses with MAC. +When adding an entry to a macipmap set, you must specify the entry as +.I IP%MAC +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. +.SS portmap +The portmap set type uses a memory range, where each bit represents +one port. A portmap set type can store up to 65535 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 fixed size hash to store the IP addresses +and can store up to 65535 (size of a B-class network) addresses. The +iphash set type is fast and great for use to store +random addresses. By supplyig the +.B "--netmask" +option with a CIDR netmask value between 0-32 at creating the set, +you will be able to store and match network addresses instead: i.e +an IP address will be in the set if the value of the address +masked with the specified netmask can be found in the set. +When adding and IP address to the hash, the IP address can be preceded +by `+', by which you can force to overwrite already existing entries +in the hash. +.P +In help mode you can use the +.B "--hint" +option to find the the smallest hashsize with the corresponding initval +for your hash entries. +.P +Options to use when creating an iphash set: +.TP +.BR "--hashsize " hashsize +.TP +.BR "--initval " hash-initval +Create an iphash set with the specified hashsize and initial +(random) hash parameter. (The +.B "--initval" +parameter is optional.) +.TP +.BR "--netmask " CIDR-netmask +When the optional +.B "--netmask" +parameter specified, network addresses will be +stored in the set instead of IP addresses. +.TP +The possible help mode options are: +.TP +.BI "-i, --hint" +Enable hinting mode to find the best (smallest) hash size. The entries +are fed via standard input. +.TP +.BI "--try " number +How many times should the program try the same hash size with +different random initvals (default 8). +.TP +.BI "--factor " number +The starting hash size is so many times the number of the entries +(default 4). +.SH 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 .. @@ -0,0 +1,2326 @@ +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/socket.h> +#include <ctype.h> +#include <stdlib.h> +#include <arpa/inet.h> +#include <stdarg.h> +#include <netdb.h> +#include <dlfcn.h> +#include <asm/bitops.h> + +#include "ipset.h" + +/* The list of all sets */ +static struct set *all_sets = NULL; + +/* The list of loaded set types */ +static struct settype *all_settypes = NULL; + +/* Suppress output to stdout and stderr? */ +static int option_quiet = 0; + +/* Data from the command line: */ +static char *set_name = NULL; /* Name of the set */ +static char *set_typename[IP_SET_LEVELS]; /* Set typenames */ +static unsigned int set_typename_level = 0; /* Size of set_typename */ +static ip_set_ip_t set_ip[IP_SET_LEVELS]; /* IP addresses of child set */ +static unsigned int set_level = 0; /* Size of set_ip */ +static unsigned int ip_level = 0; /* Size of add/del/test addresses */ + +/* Max size of a set when hinting. */ +#define MAX_HINT_SIZE 65536 + +#ifdef IP_SET_DEBUG +int option_debug = 0; +#endif + +#define OPTION_OFFSET 256 +static unsigned int global_option_offset = 0; + +/* Now most of these command parsing functions are borrowed from iptables.c */ + +/* Commands */ +#define CMD_NONE 0x0000U +#define CMD_CREATE 0x0001U /* -N */ +#define CMD_DESTROY 0x0002U /* -X */ +#define CMD_FLUSH 0x0004U /* -F */ +#define CMD_RENAME 0x0008U /* -E */ +#define CMD_SWAP 0x0010U /* -W */ +#define CMD_LIST 0x0020U /* -L */ +#define CMD_SAVE 0x0040U /* -S */ +#define CMD_RESTORE 0x0080U /* -R */ +#define CMD_ADD 0x0100U /* -A */ +#define CMD_DEL 0x0200U /* -D */ +#define CMD_TEST 0x0400U /* -T */ +#define CMD_HELP 0x0800U /* -H */ +#define CMD_VERSION 0x1000U /* -V */ +#define CMD_CREATE_CHILD 0x2000U /* -N !!! */ +#define NUMBER_OF_CMD 13 +static const char cmdflags[] = { + 'N', 'X', 'F', 'E', 'W', 'L', 'S', 'R', + 'A', 'D', 'T', 'H', 'V', +}; + +/* Options */ +#define OPT_NONE 0x0000U +#define OPT_NUMERIC 0x0001U /* -n */ +#define OPT_SORTED 0x0002U /* -s */ +#define OPT_QUIET 0x0004U /* -q */ +#define OPT_DEBUG 0x0008U /* -z */ +#define OPT_CHILDSETS 0x0010U /* -c */ +#define OPT_HINT 0x0020U /* -i */ +#define NUMBER_OF_OPT 6 +static const char optflags[] = + { 'n', 's', 'q', 'z', 'c', 'i' }; + +static struct option opts_long[] = { + /* set operations */ + {"create", 1, 0, 'N'}, + {"destroy", 2, 0, 'X'}, + {"flush", 2, 0, 'F'}, + {"rename", 1, 0, 'E'}, + {"swap", 1, 0, 'W'}, + {"list", 2, 0, 'L'}, + + {"save", 2, 0, 'S'}, + {"restore", 0, 0, 'R'}, + + /* ip in set operations */ + {"add", 1, 0, 'A'}, + {"del", 1, 0, 'D'}, + {"test", 1, 0, 'T'}, + + /* free options */ + {"numeric", 0, 0, 'n'}, + {"sorted", 0, 0, 's'}, + {"quiet", 0, 0, 'q'}, + {"childsets",0, 0, 'c'}, + {"hint", 0, 0, 'i'}, + +#ifdef IP_SET_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:nsqzciVh::H::"; + +/* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal (applies to + * CMD_LIST and CMD_ZERO only). + * Key: + * + compulsory + * x illegal + * optional + */ + +static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = { + /* -n -s -q -z -c -i*/ + /*CREATE*/ {'x', 'x', ' ', ' ', ' ', 'x'}, + /*DESTROY*/ {'x', 'x', ' ', ' ', 'x', 'x'}, + /*FLUSH*/ {'x', 'x', ' ', ' ', ' ', 'x'}, + /*RENAME*/ {'x', 'x', ' ', ' ', 'x', 'x'}, + /*SWAP*/ {'x', 'x', ' ', ' ', 'x', 'x'}, + /*LIST*/ {' ', ' ', 'x', ' ', ' ', 'x'}, + /*SAVE*/ {'x', 'x', ' ', ' ', 'x', 'x'}, + /*RESTORE*/ {'x', 'x', ' ', ' ', 'x', 'x'}, + /*ADD*/ {'x', 'x', ' ', ' ', 'x', 'x'}, + /*DEL*/ {'x', 'x', ' ', ' ', 'x', 'x'}, + /*TEST*/ {'x', 'x', ' ', ' ', 'x', 'x'}, + /*HELP*/ {'x', 'x', 'x', ' ', 'x', ' '}, + /*VERSION*/ {'x', 'x', 'x', ' ', 'x', 'x'}, +}; + +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 (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 = 0; j < NUMBER_OF_CMD; j++) { + if (!(command & (1 << j))) + continue; + + if (!(options & (1 << i))) { + if (commands_v_options[j][i] == '+') + exit_error(PARAMETER_PROBLEM, + "You need to supply the `-%c' " + "option for this command\n", + optflags[i]); + } else { + if (commands_v_options[j][i] != 'x') + 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) +{ + const char *ptr; + for (ptr = cmdflags; option > 1; option >>= 1, ptr++); + + return *ptr; +} + +static void set_command(int *cmd, const int newcmd) +{ + if (*cmd != CMD_NONE) + exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", + cmd2char(newcmd), cmd2char(newcmd)); + *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: malloc failed"); + 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 = 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; + + DP("ip_tohost()"); + + 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; + + DP("ip_tonetwork()"); + + if ((net = getnetbyaddr((long) ntohl(addr->s_addr), + AF_INET)) != NULL) + 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; + char *name; + + addr.s_addr = htonl(ip); + + if (!(options & OPT_NUMERIC)) { + if ((name = ip_tohost(&addr)) != NULL || + (name = ip_tonetwork(&addr)) != NULL) + return name; + } + + return inet_ntoa(addr); +} + +/* 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; + + if (inet_aton(str, &addr) != 0) { + *ip = ntohl(addr.s_addr); /* We want host byte order */ + +#ifdef IP_SET_DEBUG + { + /*DEBUG*/ char *p = (char *) ip; + DP("PARSE_IP %x %x %x %x %x %x", *ip, 0xC10BF8A6, + p[0], p[1], p[2], p[3]); + } +#endif + + 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); + +#ifdef IP_SET_DEBUG + { + /*DEBUG*/ char *p = (char *) ip; + DP("PARSE_IP %x %x %x %x %x %x", *ip, 0xC10BF8A6, + p[0], p[1], p[2], p[3]); + } +#endif + + 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("parse_mask %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 = 0xFFFFFFFF << (32 - bits); +} + +/* 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("parse_ipandmask: %s ip: %08X (%s) mask: %08X", + str, *ip, ip_tostring(*ip, 0), *mask); + + /* Apply the netmask */ + *ip &= *mask; + + DP("parse_ipandmask: %s ip: %08X (%s) mask: %08X", + str, *ip, ip_tostring(*ip, 0), *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) + && (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); +} + +static struct settype *settype_find(const char *typename) +{ + struct settype *runner = all_settypes; + + DP("settype %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 */ +} + +/* Register a new set type */ +void settype_register(struct settype *settype) +{ + struct settype *chk; + + DP("register_settype '%s'\n", settype->typename); + + /* Check if this typename already exists */ + chk = settype_find(settype->typename); + + if (chk != NULL) + exit_error(OTHER_PROBLEM, + "Set type '%s' already registred!\n", + settype->typename); + + /* Check version */ + if (settype->protocol_version != IP_SET_PROTOCOL_VERSION) + exit_error(OTHER_PROBLEM, + "Set type is of wrong protocol version %u!" + " I'm am of version %u.\n", settype->typename, + settype->protocol_version, + IP_SET_PROTOCOL_VERSION); + + /* Insert first */ + settype->next = all_settypes; + settype->data = ipset_malloc(settype->create_size); + all_settypes = settype; + + DP("ip_set: register settype end '%s'\n", settype->typename); +} + +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(int err) +{ + /* translate errnos, as returned by our *sockopt() functions */ + exit_error(OTHER_PROBLEM, "Error from kernel: %s", strerror(err)); +} + +static void kernel_sendto(void *data, size_t size) +{ + int res; + int sockfd = kernel_getsocket(); + + /* Send! */ + res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + + DP("kernel_sendto() res=%d errno=%d\n", res, errno); + + if (res != 0) + kernel_error(errno); +} + +/* Used by addip and delip that has to handle the EEXIST error better */ +static int kernel_sendto_handleexist(void *data, size_t size) +{ + int res; + int sockfd = kernel_getsocket(); + + /* Send! */ + res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + + DP("kernel_sendto_handleexist() res=%d errno=%d\n", res, errno); + + if (res != 0 && errno == EEXIST) + return -1; + else if (res != 0) + kernel_error(errno); + + return 0; /* all ok */ +} + +static void kernel_getfrom(void *data, size_t * size) +{ + int res; + int sockfd = kernel_getsocket(); + + /* Send! */ + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + + DP("kernel_getfrom() res=%d errno=%d\n", res, errno); + + if (res != 0) + kernel_error(errno); +} + +static int get_protocolversion(void) +{ + struct ip_set_req_version req_version; + size_t size = sizeof(struct ip_set_req_version); + + req_version.op = IP_SET_OP_VERSION; + + kernel_getfrom(&req_version, &size); + + DP("get_protocolversion() ver=%d", req_version.version); + + return req_version.version; +} + +static void set_append(struct set *set) +{ + struct set *entry = all_sets; + + while (entry != NULL && entry->next != NULL) + entry = entry->next; + + if (entry == NULL) + all_sets = set; + else + entry->next = set; +} + +static void get_sets(void) +{ + void *data = NULL; + struct ip_set_req_listing_size req_list; + int sockfd = kernel_getsocket(); + size_t size; + size_t eaten = 0; + int i, res; + + DP("get_sets()"); + for (i = 0; i < LIST_TRIES; i++) { + req_list.op = IP_SET_OP_LISTING_SIZE; + size = sizeof(req_list); + kernel_getfrom(&req_list, &size); + size = req_list.size; + + DP("got size: %d", size); + + if (req_list.size == 0) + return; /* No sets in kernel */ + + data = ipset_malloc(size); + ((struct ip_set_req_base *) data)->op = IP_SET_OP_LISTING; + + /* Get! */ + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, &size); + DP("list_get getsockopt() res=%d errno=%d\n", res, errno); + + if (res == 0) + goto got_sets; /* all OK */ + else if (errno != ENOMEM) + break; /* Not a memory error */ + + DP("not enough mem, looping again"); + free(data); + } + + if (errno == ENOMEM) + 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(errno); + + got_sets: + + DP("get_sets() - size=%d data=%p", size, data); + /* Handle the data */ + while (eaten < size) { + struct ip_set_req_listing *header = + (struct ip_set_req_listing *) (data + eaten); + struct set *set = ipset_malloc(sizeof(struct set)); + + memset(set, 0, sizeof(struct set)); + + DP("fillin %d %p", eaten, header); + +#ifdef IP_SET_DEBUG + /* DEBUG */ + { + void *i; + + DP("SET DATA:"); + + for (i = data + eaten; + i < + data + eaten + + sizeof(struct ip_set_req_listing); i += 4) { + unsigned *j = (unsigned *) i; + DP("%x", *j); + } + } +#endif + + /* Fill in data */ + DP("Processing set '%s'", header->name); + strcpy(set->name, header->name); + set->id = header->id; + set->levels = header->levels; + set->ref = header->ref; + for (i = 0; i < set->levels; i++) { + set->settype[i] = settype_load(header->typename[i]); + set->private[i].adt = + ipset_malloc(set->settype[i]->req_size); + } + + eaten += sizeof(struct ip_set_req_listing); + + DP("insert in list"); + set_append(set); + } + + DP("free"); + + free(data); + + DP("get_sets() eaten = %d, size = %d", eaten, size); + + if (eaten != size) + exit_error(OTHER_PROBLEM, + "Desynched in listing of sets from kernel. " + "Read %d bytes. Worked %d bytes.", size, eaten); +} + +struct set *set_find(const char *setname) +{ + struct set *set = all_sets; + + DP("%s", setname); + + while (set != NULL) { + if (strcmp(setname, set->name) == 0) + return set; + set = set->next; + } + return NULL; +} + +static struct set *set_checkname(const char *setname) +{ + char *saved = strdup(setname); + char *ptr, *tmp = saved; + struct set *set; + int i; + + DP("%s", setname); + + /* Cleanup */ + if (set_name != NULL) + free(set_name); + + for (i = 0; i < IP_SET_SETIP_LEVELS; i++) + set_ip[i] = 0; + set_level = 0; + + /* name[:ip,...] */ + ptr = strsep(&tmp, ":"); + if (strlen(ptr) > IP_SET_MAXNAMELEN - 1) + exit_error(PARAMETER_PROBLEM, + "Setname '%s' in '%s' too long. Max %d characters.", + ptr, setname, IP_SET_MAXNAMELEN - 1); + + DP("%s (%s)", ptr, tmp); + set = set_find(ptr); + if (!set && tmp) + exit_error(PARAMETER_PROBLEM, + "Set '%s' not found for '%s'\n", + ptr, setname); + + set_name = strdup(ptr); + + while (set_level < IP_SET_SETIP_LEVELS && tmp) { + ptr = strsep(&tmp, ","); + switch (set->settype[set_level]->typecode) { + case IPSET_TYPE_IP: + parse_ip(ptr, &set_ip[set_level++]); + break; + case IPSET_TYPE_PORT: + parse_port(ptr, &set_ip[set_level++]); + break; + default: + ; /* Keep compilers happy */ + } + if (set->levels <= set_level) + exit_error(PARAMETER_PROBLEM, + "Subset definition is too deep for set '%s'\n", + set_name); + } + + free(saved); + return set; +} + +static inline struct set *set_find_byname(const char *setname) +{ + struct set *set = set_checkname(setname); + + if (!set) + exit_error(PARAMETER_PROBLEM, + "Set '%s' not found\n", setname); + return set; +} + +static void set_checktype(const char *typename) +{ + char *saved = strdup(typename); + char *ptr, *tmp = saved; + int i; + + /* Cleanup */ + for (i = 0; i < IP_SET_LEVELS; i++) + if (set_typename[i] != NULL) + free(set_typename[i]); + + /* typename[,...] */ + set_typename_level = 0; + while (set_typename_level < IP_SET_LEVELS && tmp) { + ptr = strsep(&tmp, ","); + DP("settype '%s', level %i", ptr, set_typename_level); + if (strlen(ptr) > IP_SET_MAXNAMELEN - 1) + exit_error(PARAMETER_PROBLEM, + "Typename '%s' in '%s' too long. Max %d characters.", + ptr, typename, IP_SET_MAXNAMELEN - 1); + set_typename[set_typename_level++] = strdup(ptr); + } + DP("tmp '%s', level %i", tmp, set_typename_level); + if (set_typename_level >= IP_SET_LEVELS || tmp) + exit_error(PARAMETER_PROBLEM, + "More than %d settypes in '%s'.", + IP_SET_LEVELS - 1 , typename); + free(saved); +} + +/* Get setid from kernel. */ +static void +set_get_setid(struct set *set) +{ + struct ip_set_req_get req_get; + size_t size = sizeof(struct ip_set_req_get); + + DP("set_get_setid()"); + + req_get.op = IP_SET_OP_GETSET_BYNAME; + strcpy(req_get.name, set->name); + + /* Result in the id-field */ + kernel_getfrom(&req_get, &size); + + if (size != sizeof(struct ip_set_req_get)) + exit_error(OTHER_PROBLEM, + "Incorrect return size from kernel." + "Should be %d but was %d.", + sizeof(struct ip_set_req_get), size); + + DP("set_get_setid() result=%u", req_get.id); + + if (req_get.id < 0) + exit_error(OTHER_PROBLEM, + "Set %s cannot be found.", + set->name); + + set->id = req_get.id; + set->ref = req_get.ref; +} + +/* Send create set order to kernel */ +static void +set_create(struct set *set) +{ + struct ip_set_req_create req_create; + struct settype *settype = set->settype[0]; + size_t size; + void *data; + int i; + + DP("set_create()"); + + req_create.op = IP_SET_OP_CREATE; + strcpy(req_create.name, set->name); + for (i = 0; i < set_typename_level; i++) + strcpy(req_create.typename[i], set->settype[i]->typename); + req_create.levels = set_typename_level; + + /* Final checks */ + settype->create_final(settype->data, settype->flags); + + /* Alloc memory for the data to send */ + size = sizeof(struct ip_set_req_create) + settype->create_size; + data = ipset_malloc(size); + + /* Add up ip_set_req_create and the settype data */ + memcpy(data, &req_create, sizeof(struct ip_set_req_create)); + memcpy(data + sizeof(struct ip_set_req_create), + settype->data, settype->create_size); + +#ifdef IP_SET_DEBUG + /* DEBUG */ + { + void *i; + + DP("CMD_CREATE"); + DP("OP %u", req_create.op); + DP("Name: %s", req_create.name); + DP("Typename: %s", req_create.typename[0]); + DP("All data"); + + for (i = data; i < data + size; i += 4) { + unsigned *j = (unsigned *) i; + DP("%x", *j); + } + } +#endif + + kernel_sendto(data, size); + free(data); + + /* Get set id from kernel */ + set_get_setid(set); + /* Success! */ + set_append(set); +} + +/* Send create childset order to kernel */ +static void +set_create_childset(const struct set *set, unsigned options) +{ + struct ip_set_req_sub req_sub; + struct settype *settype = set->settype[set_level]; + size_t size; + void *data; + int i; + + DP("childset_create()"); + + req_sub.op = IP_SET_OP_CREATE_CHILD; + req_sub.id = set->id; + for (i = 0; i < set_level; i++) + req_sub.ip[i] = set_ip[i]; + req_sub.level = set_level; + req_sub.childsets = options & OPT_CHILDSETS; + + /* Final checks */ + settype->create_final(settype->data, settype->flags); + + /* Alloc memory for the data to send */ + size = sizeof(struct ip_set_req_sub) + settype->create_size; + data = ipset_malloc(size); + + /* Add up ip_set_req_sub and the settype data */ + memcpy(data, &req_sub, sizeof(struct ip_set_req_sub)); + memcpy(data + sizeof(struct ip_set_req_sub), + settype->data, settype->create_size); + +#ifdef IP_SET_DEBUG + /* DEBUG */ + { + void *i; + + DP("CMD_CREATE_CHILD"); + DP("OP %u", req_sub.op); + DP("Id: %u", req_sub.id); + DP("All data"); + + for (i = data; i < data + size; i += 4) { + unsigned *j = (unsigned *) i; + DP("%x", *j); + } + } +#endif + + kernel_sendto(data, size); + free(data); +} + +static void set_del(struct set *set) +{ + int i; + + for (i = 0; i < set->levels; i++) { + if (set->private[i].setdata) + set->settype[i]->killmembers(&set->private[i].setdata); + ipset_free(&set->private[i].bitmap); + } + + free(set); +} + +/* Sends destroy order to kernel for one or all sets + * All sets: set == NULL + */ +static void set_destroy(struct set *set) +{ + struct ip_set_req_std req_destroy; + + req_destroy.op = IP_SET_OP_DESTROY; + + if (set == NULL) { + /* Do them all */ + + while (all_sets != NULL) { + set = all_sets; + all_sets = set->next; + req_destroy.id = set->id; + req_destroy.level = 0; + kernel_sendto(&req_destroy, + sizeof(struct ip_set_req_std)); + set_del(set); + } + } else { + int i; + + DP("destroy %s", set->name); + + /* Only destroy one */ + req_destroy.id = set->id; + for (i = 0; i < set_level; i++) + req_destroy.ip[i] = set_ip[i]; + req_destroy.level = set_level; + kernel_sendto(&req_destroy, + sizeof(struct ip_set_req_std)); + if (set_level == 0) { + if (set == all_sets) + all_sets = set->next; + else { + struct set *entry = all_sets; + + while (entry && entry->next && entry->next != set) + entry = entry->next; + if (entry->next == set) + entry->next = set->next; + } + set_del(set); + } + } +} + +/* Sends flush order to kernel for one or all sets + * All sets: set = NULL + */ +static void set_flush(const struct set *set, unsigned options) +{ + struct ip_set_req_sub req_flush; + + DP("flush"); + + req_flush.op = IP_SET_OP_FLUSH; + + if (set == NULL) { + /* Do them all */ + struct set *entry = all_sets; + + while (entry != NULL) { + req_flush.id = entry->id; + req_flush.level = 0; + req_flush.childsets = options & OPT_CHILDSETS; + kernel_sendto(&req_flush, + sizeof(struct ip_set_req_sub)); + entry = entry->next; + } + } else { + int i; + + /* Only one */ + req_flush.id = set->id; + for (i = 0; i < set_level; i++) + req_flush.ip[i] = set_ip[i]; + req_flush.level = set_level; + req_flush.childsets = options & OPT_CHILDSETS; + kernel_sendto(&req_flush, sizeof(struct ip_set_req_sub)); + } +} + +/* Sends rename order to kernel */ +static void set_rename(struct set *set, const char *newname) +{ + struct ip_set_req_rename req_rename; + + DP("rename"); + + req_rename.op = IP_SET_OP_RENAME; + req_rename.id = set->id; + strncpy(req_rename.newname, newname, IP_SET_MAXNAMELEN); + + DP("rename - send"); + kernel_sendto(&req_rename, sizeof(struct ip_set_req_rename)); + + strncpy(set->name, newname, IP_SET_MAXNAMELEN); +} + +/* Sends swap order to kernel for two sets */ +static void set_swap(struct set *from, struct set *to) +{ + struct ip_set_req_swap req_swap; + + DP("swap"); + + req_swap.op = IP_SET_OP_SWAP; + req_swap.id = from->id; + req_swap.to = to->id; + + DP("swap - send"); + kernel_sendto(&req_swap, sizeof(struct ip_set_req_swap)); + + from->id = req_swap.to; + to->id = req_swap.id; +} + +static void *list_get(int opsize, int opget, ip_set_ip_t id, + int *size, ip_set_ip_t *ip, unsigned level) +{ + int res, i; + struct ip_set_req_list req_list; + struct ip_set_req_std *req_data; + int sockfd = kernel_getsocket(); + void *data = NULL; + + req_list.op = opsize; + req_list.id = id; + req_list.level = level; + for (i = 0; i < level; i++) + req_list.ip[i] = ip[i]; + + *size = sizeof(req_list); + kernel_getfrom(&req_list, size); + + DP("got size: %d", req_list.size); + + if (req_list.size == 0) + return data; + + *size = sizeof(struct ip_set_req_std) > req_list.size ? + sizeof(struct ip_set_req_std) : req_list.size; + + data = ipset_malloc(*size); + req_data = (struct ip_set_req_std *) data; + req_data->op = opget; + req_data->id = id; + req_data->level = level; + for (i = 0; i < level; i++) + req_data->ip[i] = ip[i]; + + /* Get! */ + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); + DP("list_get getsockopt() res=%d errno=%d\n", res, errno); + + if (res != 0) + kernel_error(errno); + + /* All OK, return */ + return data; +} + +static void list_getheaders(struct set *set, ip_set_ip_t *ip, unsigned level) +{ + void *data; + size_t size; + + data = list_get(IP_SET_OP_LIST_HEADER_SIZE, + IP_SET_OP_LIST_HEADER, + set->id, &size, ip, level); + + if (size == 0) + exit_error(OTHER_PROBLEM, + "Kernel returned zero header size. " + "Something screwed up."); + + /* Cleanup setdata */ + if (set->private[level].setdata) + set->settype[level]->killmembers(&set->private[level].setdata); + + /* Handle the data */ + set->settype[level]->initheader(&set->private[level].setdata, + data, size); + + ipset_free(&data); +} + +static void list_getmembers(struct set *set, ip_set_ip_t *ip, unsigned level) +{ + void *data; + size_t size; + + data = list_get(IP_SET_OP_LIST_MEMBERS_SIZE, + IP_SET_OP_LIST_MEMBERS, + set->id, &size, ip, level); + + if (size == 0) + exit_error(OTHER_PROBLEM, + "Kernel returned zero header size. " + "Something screwed up."); + + /* Handle the data */ + set->settype[level]->initmembers(set->private[level].setdata, + data, size); + + /* Next list_getheaders or set_del frees the data */ +} + +static void list_getchildsets(struct set *set, ip_set_ip_t *ip, unsigned level) +{ + void *data; + size_t size; + + data = list_get(IP_SET_OP_LIST_CHILDSETS_SIZE, + IP_SET_OP_LIST_CHILDSETS, + set->id, &size, ip, level); + + /* Cleanup */ + ipset_free(&set->private[level].bitmap); + set->private[level].bitmap = NULL; + + if (size == 0) + return; /* No child set */ + + /* Handle the data */ + set->private[level].bitmap = data; + + /* Next list_getchildsets or set_del frees the data */ +} + +/* Print a child set */ +static void set_list_childset(struct set *set, + unsigned options, + ip_set_ip_t *ip, + unsigned level) +{ + int i; + ip_set_ip_t id; + + /* Load the set header, member and childset data */ + list_getheaders(set, ip, level); + list_getmembers(set, ip, level); + list_getchildsets(set, ip, level); + + /* Pretty print the childset */ + printf("Childset %s %s", + set->private[level].bitmap != NULL ? "+" : "-", + set->name); + for (i = 0; i < level; i++) { + switch (set->settype[i]->typecode) { + case IPSET_TYPE_IP: + printf("%s%s", i == 0 ? ":" : ",", + ip_tostring(ip[i], options)); + break; + case IPSET_TYPE_PORT: + printf("%s%s", i == 0 ? ":" : ",", + port_tostring(ip[i], options)); + break; + default: + ; + } + } + printf("\n"); + + /* Pretty print the type header */ + set->settype[level]->printheader(set->private[level].setdata, options); + + /* Pretty print all IPs */ + if (options & OPT_SORTED) + set->settype[level]->printips_sorted(set->private[level].setdata, options); + else + set->settype[level]->printips(set->private[level].setdata, options); + + /* Pretty print all childsets. */ + if (!(set->private[level].bitmap != NULL && (options & OPT_CHILDSETS))) + return; + + for (id = 0; + id < set->settype[level]->sizeid(set->private[level].setdata); + id++) { + if (test_bit(id, set->private[level].bitmap)) { + ip[level] = set->settype[level]->getipbyid( + set->private[level].setdata, id); + set_list_childset(set, options, ip, level+1); + } + } +} + +/* Help function to set_list() */ +static void set_list_oneset(struct set *set, + unsigned options) +{ + int i; + + /* Pretty print the set */ + printf("Name %s\n", set->name); + printf("Type %s", set->settype[0]->typename); + for (i = 1; i < set->levels; i++) + printf(",%s", set->settype[i]->typename); + printf("\n"); + printf("References %d\n", set->ref); + + /* Pretty print the childset */ + set_list_childset(set, options, set_ip, set_level); + + printf("\n"); /* One newline between sets */ +} + +/* Print a set or all sets + * All sets: set = NULL + */ +static void set_list(struct set *set, unsigned options) +{ + if (set == NULL) { + set = all_sets; + + while (set != NULL) { + set_list_oneset(set, options); + set = set->next; + } + } else + set_list_oneset(set, options); +} + +/* Performs a save to stdout + * All sets are marked with set.name[0] == '\0' + * Area pointed by 'set' will be changed + */ +static void set_save(const struct set *set) +{ + DP("set_save() not yet implemented"); +} + +/* Performs a restore from stdin */ +static void set_restore() +{ + DP("set_restore() not yet implemented"); +} + +static void parse_adt_ip(int cmd, struct set *set, const char *adt_ip) +{ + char *saved = strdup(adt_ip); + char *ptr, *tmp = saved; + + ip_level = set_level; + + while (ip_level <= set->levels && tmp) { + + if (ip_level > set_level) + list_getheaders(set, set_ip, ip_level); + + ptr = strsep(&tmp, ","); + + /* Call the parser function */ + set_ip[ip_level] = set->settype[ip_level]->adt_parser( + cmd, ptr, + set->private[ip_level].adt, + set->private[ip_level].setdata); + DP("%i: (%s)", ip_level, ip_tostring(set_ip[ip_level], 0)); + ip_level++; + } + + if (tmp || ip_level > set->levels) + exit_error(PARAMETER_PROBLEM, + "Specified (child) set and IP levels together" + " are deeper than %s set (%i).", + set->name, set->levels); + + free(saved); +} + +/* Sends addip order to kernel for a set */ +static void set_addip(struct set *set, const char *adt_ip) +{ + struct ip_set_req_std req_addip; + size_t size, offset; + void *data; + int i; + + DP("set_addip() %p", set); + + list_getheaders(set, set_ip, set_level); + + parse_adt_ip(ADT_ADD, set, adt_ip); + + req_addip.op = IP_SET_OP_ADD_IP; + req_addip.id = set->id; + for (i = 0; i < set_level; i++) + req_addip.ip[i] = set_ip[i]; + req_addip.level = set_level; + + DP("%i %i", set_level, ip_level); + /* Alloc memory for the data to send */ + DP("alloc"); + size = sizeof(struct ip_set_req_std); + for (i = set_level; i < ip_level; i++) { + size += set->settype[i]->req_size; + DP("i: %i, size: %u", i, size); + } + data = ipset_malloc(size); + + /* Add up req_addip and the settype data */ + DP("mem"); + memcpy(data, &req_addip, sizeof(struct ip_set_req_std)); + offset = sizeof(struct ip_set_req_std); + for (i = set_level; i < ip_level; i++) { + memcpy(data + offset, + set->private[i].adt, + set->settype[i]->req_size); + offset += set->settype[i]->req_size; + } + +#ifdef IP_SET_DEBUG + /* DEBUG */ + { + void *i; + + DP("CMD_ADDIP"); + DP("OP %u", req_addip.op); + DP("Id: %u", req_addip.id); + DP("All data"); + + for (i = data; i < data + size; i += 4) { + unsigned *j = (unsigned *) i; + DP("%x", *j); + } + } +#endif + + if (kernel_sendto_handleexist(data, size) == -1) + /* Arghh, we can't get the commandline string anymore, oh well */ + exit_error(OTHER_PROBLEM, "Already added in set %s.", + set->name); + free(data); +} + +/* Sends delip order to kernel for a set */ +static void set_delip(struct set *set, const char *adt_ip, unsigned options) +{ + struct ip_set_req_std req_delip; + size_t size, offset; + void *data; + int i; + + DP("set_delip()"); + + list_getheaders(set, set_ip, set_level); + + parse_adt_ip(ADT_DEL, set, adt_ip); + + req_delip.op = IP_SET_OP_DEL_IP; + req_delip.id = set->id; + for (i = 0; i < set_level; i++) + req_delip.ip[i] = set_ip[i]; + req_delip.level = set_level; + + /* Alloc memory for the data to send */ + size = sizeof(struct ip_set_req_std); + for (i = set_level; i < ip_level; i++) + size += set->settype[i]->req_size; + data = ipset_malloc(size); + + /* Add up req_sub and the settype data */ + memcpy(data, &req_delip, sizeof(struct ip_set_req_std)); + offset = sizeof(struct ip_set_req_std); + for (i = set_level; i < ip_level; i++) { + memcpy(data + offset, + set->private[i].adt, + set->settype[i]->req_size); + offset += set->settype[i]->req_size; + } + +#ifdef IP_SET_DEBUG + /* DEBUG */ + { + void *i; + + DP("CMD_DELIP"); + DP("OP %u", req_delip.op); + DP("Id: %u", req_delip.id); + DP("All data"); + + for (i = data; i < data + size; i += 4) { + unsigned *j = (unsigned *) i; + DP("%x", *j); + } + } +#endif + + if (kernel_sendto_handleexist(data, size) == -1) + /* Arghh, we can't get the commandline string anymore, oh well */ + exit_error(OTHER_PROBLEM, "Doesn't exist in set %s.", + set->name); + free(data); +} + +/* Sends test order to kernel for a set */ +static int +set_testip(struct set *set, const char *name, const char *adt_ip) +{ + int i, res; + struct ip_set_req_test req_test; + void *data; + size_t size, offset; + + list_getheaders(set, set_ip, set_level); + + parse_adt_ip(ADT_TEST, set, adt_ip); + + req_test.op = IP_SET_OP_TEST_IP; + req_test.id = set->id; + for (i = 0; i < set_level; i++) + req_test.ip[i] = set_ip[i]; + req_test.level = set_level; + + /* Alloc memory for the data to send */ + size = sizeof(struct ip_set_req_test); + for (i = set_level; i < ip_level; i++) + size += set->settype[i]->req_size; + data = ipset_malloc(size); + + /* Add up req_test and the settype data */ + memcpy(data, &req_test, sizeof(struct ip_set_req_test)); + offset = sizeof(struct ip_set_req_test); + for (i = set_level; i < ip_level; i++) { + memcpy(data + offset, + set->private[i].adt, + set->settype[i]->req_size); + offset += set->settype[i]->req_size; + } + /* Result in the op-field */ + kernel_getfrom(data, &size); + + if (size != sizeof(struct ip_set_req_test)) + exit_error(OTHER_PROBLEM, + "Incorrect return size from kernel." + "Should be %d but was %d.", + sizeof(struct ip_set_req_test), size); + + DP("set_testip() result=%x", req_test.op); + + if (((struct ip_set_req_test *)data)->reply > 0) { + ipset_printf("%s is in set %s.", adt_ip, name); + res = 0; /* Return value for the program */ + } else { + ipset_printf("%s is NOT in set %s.", adt_ip, name); + res = 1; + } + + free(data); + return res; +} + +/* Prints help + * If settype is non null help for that type is printed as well + */ +static void set_help(const struct settype *settype) +{ +#ifdef IP_SET_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 -[ADT] set entry\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); + + printf("Commands:\n" + "Either long or short options are allowed.\n" + " --create -N setname settype0[,settype1,...] <options>\n" + " Create a new set\n" + " --create -N setname:IP[,IP...] settype <options>\n" + " Create childset at setname:IP[,IP...]\n" + " --destroy -X [setname:IP,....]\n" + " Destroy a (child)set or all sets\n" + " --flush -F [setname:IP,...] [options]\n" + " Delete a (child)set or all sets\n" + " --rename -E from-set to-set\n" + " Rename from-set to to-set\n" + " --swap -W from-set to-set\n" + " Swap the content of two existing sets\n" + " --list -L [setname:IP,...] [options]\n" + " List the entries in a (child)set or all sets\n" + " --save -S [setname]\n" + " Save the set or all sets to stdout\n" + " --restore -R\n" + " Restores from stdin a saved state\n" + " --add -A setname[:IP,...] entry[,entry...]\n" + " Add an entry to a (child)set\n" + " --del -D setname[:IP,...] entry[,entry...]\n" + " Deletes an entry from a (child)set\n" + " --test -T setname[:IP,...] entry[,entry...]\n" + " Tests if an entry exists in a (child)set.\n" + " --help -H [settype] [options]]\n" + " 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" + " --childsets -c Operation valid for child sets\n" + " --hint -i Hint best settype initialization parameters\n"); + printf(debughelp); + + if (settype != NULL) { + printf("Type '%s' specific:\n", settype->typename); + settype->usage(); + } +} + +/* Hint various infos on a given settype */ +static int +settype_hint(const struct settype *settype, unsigned options) +{ + char buffer[1024]; + ip_set_ip_t ip[MAX_HINT_SIZE]; + ip_set_ip_t id = 0; + + if (!settype->hint) + return 0; + + while (fgets(buffer, sizeof(buffer), stdin) != NULL + && id < MAX_HINT_SIZE) { + if (buffer[0] == '\n' || buffer[0] == '#') + continue; + switch (settype->typecode) { + case IPSET_TYPE_IP: + parse_ip(buffer, &ip[id++]); + break; + case IPSET_TYPE_PORT: + parse_port(buffer, &ip[id++]); + break; + default: + ; + } + } + if (id >= MAX_HINT_SIZE) + exit_error(OTHER_PROBLEM, + "More than %ld entries, exiting.", + MAX_HINT_SIZE); + if (id < 1) + exit_error(OTHER_PROBLEM, + "No input, no hint."); + + settype->hint(settype->data, ip, id); + + return 0; +} + +static void init_sets(void) +{ + int version; + static int done = 0; + + if (done) + return; + + version = get_protocolversion(); + if (version != IP_SET_PROTOCOL_VERSION) + exit_error(OTHER_PROBLEM, + "Kernel ipset code is of protocol version %u." + "I'm of protocol version %u.\n" + "Please upgrade your kernel and/or ipset(8) utillity.", + version, IP_SET_PROTOCOL_VERSION); + + /* Get the list of existing sets from the kernel */ + get_sets(); + done = 1; +} + +/* Main worker function */ +int parse_commandline(int argc, char *argv[], int exec_restore) +{ + int res = 0; + unsigned command = 0; + unsigned options = 0; + int c; + + struct set *set = NULL; + struct set *set_to = NULL; /* Used by -W */ + struct settype *settype = NULL; /* Used by -H */ + char *name = NULL; /* Used by -E */ + char *entries = NULL; /* Used by -A, -D, -T */ + + struct option *opts = opts_long; + + /* Suppress error messages: we may add new options if we + demand-load a protocol. */ + opterr = 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 */ + set_command(&command, CMD_HELP); + + if (optarg) + set_checktype(optarg); + else if (optind < argc + && argv[optind][0] != '-') + set_checktype(argv[optind++]); + + if (!set_typename_level) + break; + + if (set_typename_level != 1) + exit_error(PARAMETER_PROBLEM, + "-%c requires one settype as argument", + cmd2char(CMD_HELP)); + + settype = settype_load(set_typename[0]); + + /* Merge the hint options */ + if (settype->hint_parse) { + opts = merge_options(opts, + settype->hint_opts, + &settype->option_offset); + + /* Reset space for settype create data */ + memset(settype->data, 0, settype->hint_size); + + /* Zero the flags */ + settype->flags = 0; + + DP("call hint_init"); + /* Call the settype hint_init */ + settype->hint_init(settype->data); + } + + break; + } + + case 'V':{ /* Version */ + /* Dont display kernel protocol version because + * that might generate errors if the ipset module + * is not loaded in.*/ + printf("%s v%s Protocol version %u.\n", + program_name, program_version, + IP_SET_PROTOCOL_VERSION); + exit(0); + } + + case 'N':{ /* Create */ + char *typename = NULL; + int i; + + init_sets(); + + DP("check setname"); + /* setname */ + set = set_checkname(optarg); + + if (set_level == 0) { + /* New set to be created */ + + DP(" new set, set_level == 0"); + + if (set != NULL) + exit_error(OTHER_PROBLEM, + "Set %s already exists.", + set_name); + set = ipset_malloc(sizeof(struct set)); + memset(set, 0, sizeof(struct set)); + + set_command(&command, CMD_CREATE); + + /* typename */ + if (optind < argc + && argv[optind][0] != '-') + typename = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires new-setname and settype", + cmd2char(CMD_CREATE)); + + DP(" check typename"); + set_checktype(typename); + + strcpy(set->name, set_name); + set->levels = set_typename_level; + + DP("load the settypes"); + for (i = 0; i < set_typename_level; i++) + set->settype[i] = settype_load(set_typename[i]); + + } else {/* set_level != 0 */ + /* New childset to be created */ + + DP(" new childset, set_level != 0"); + + if (set == NULL) + exit_error(OTHER_PROBLEM, + "Set %s does not exist.", + set_name); + set_command(&command, CMD_CREATE_CHILD); + + /* typename */ + if (optind < argc + && argv[optind][0] != '-') + typename = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires new-setname and settype", + cmd2char(CMD_CREATE)); + + DP(" check typename"); + set_checktype(typename); + if (set_typename_level > 1) + exit_error(PARAMETER_PROBLEM, + "-%c requires single settype specified", + cmd2char(CMD_CREATE)); + else if (set_level >= set->levels) + exit_error(PARAMETER_PROBLEM, + "specified childset is deeper than " + "%s set itself.", set->name); + else if (strcmp(typename, set->settype[set_level]->typename)) + exit_error(PARAMETER_PROBLEM, + "settype '%s' must be used instead of " + "'%s' in childset '%s'", + set->settype[set_level]->typename, + typename, optarg); + } + + DP("merge options"); + /* Merge the create options */ + opts = merge_options(opts, + set->settype[set_level]->create_opts, + &set->settype[set_level]->option_offset); + + /* Reset space for settype create data */ + memset(set->settype[set_level]->data, 0, + set->settype[set_level]->create_size); + + /* Zero the flags */ + set->settype[set_level]->flags = 0; + + DP("call create_init"); + /* Call the settype create_init */ + set->settype[set_level]->create_init( + set->settype[set_level]->data); + + break; + } + + case 'X':{ /* Destroy */ + init_sets(); + + set_command(&command, CMD_DESTROY); + + if (optarg) + set = set_find_byname(optarg); + else if (optind < argc + && argv[optind][0] != '-') + set = set_find_byname(argv[optind++]); + else + set = NULL; /* Mark to destroy all (empty) sets */ + + if (set && set_level >= set->levels) + exit_error(PARAMETER_PROBLEM, + "specified childset is deeper than " + "%s set itself.", set->name); + break; + } + + case 'F':{ /* Flush */ + init_sets(); + + set_command(&command, CMD_FLUSH); + + DP("flush: %s", optarg); + + if (optarg) + set = set_find_byname(optarg); + else if (optind < argc + && argv[optind][0] != '-') + set = set_find_byname(argv[optind++]); + else + set = NULL; /* Mark to flush all */ + + if (set && set_level >= set->levels) + exit_error(PARAMETER_PROBLEM, + "specified childset is deeper than " + "%s set itself.", set->name); + break; + } + + case 'E':{ /* Rename */ + init_sets(); + + set_command(&command, CMD_RENAME); + + set = set_find_byname(optarg); + if (set_level) + exit_error(PARAMETER_PROBLEM, + "childsets cannot be swapped"); + + if (optind < argc + && argv[optind][0] != '-') + name = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a setname " + "and the new name for that set", + cmd2char(CMD_RENAME)); + if (strlen(name) > IP_SET_MAXNAMELEN - 1) + exit_error(PARAMETER_PROBLEM, + "Setname '%s' is too long. Max %d characters.", + name, IP_SET_MAXNAMELEN - 1); + + /* Set with new name must not exist. */ + set_to = set_checkname(name); + if (set_to) + exit_error(PARAMETER_PROBLEM, + "Set already exists, cannot rename to %s", + name); + if (set_level) + exit_error(PARAMETER_PROBLEM, + "childsets cannot be swapped"); + + break; + } + + case 'W':{ /* Swap */ + char *name = NULL; + + init_sets(); + + set_command(&command, CMD_SWAP); + + set = set_find_byname(optarg); + if (set_level) + exit_error(PARAMETER_PROBLEM, + "childsets cannot be swapped"); + + if (optind < argc + && argv[optind][0] != '-') + name = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires the names of two " + "existing sets", + cmd2char(CMD_SWAP)); + if (strlen(name) > IP_SET_MAXNAMELEN - 1) + exit_error(PARAMETER_PROBLEM, + "Setname '%s' is too long. Max %d characters.", + name, IP_SET_MAXNAMELEN - 1); + + /* Both sets must exist. */ + set_to = set_find_byname(name); + if (set_level) + exit_error(PARAMETER_PROBLEM, + "childsets cannot be swapped"); + + break; + } + + case 'L':{ /* List */ + init_sets(); + + set_command(&command, CMD_LIST); + if (optarg) + set = set_find_byname(optarg); + else if (optind < argc + && argv[optind][0] != '-') + set = set_find_byname(argv[optind++]); + else + set = NULL; /* Mark all */ + + if (set && set_level >= set->levels) + exit_error(PARAMETER_PROBLEM, + "specified childset is deeper than " + "%s set itself.", set->name); + break; + } + + case 'S':{ /* Save */ + init_sets(); + + set_command(&command, CMD_SAVE); + if (optarg) + set = set_find_byname(optarg); + else if (optind < argc + && argv[optind][0] != '-') + set = set_find_byname(argv[optind++]); + else + set = NULL; /* Mark to save all */ + + if (set && set_level >= set->levels) + exit_error(PARAMETER_PROBLEM, + "specified childset is deeper than " + "%s set itself.", set->name); + break; + } + + case 'R':{ /* Restore */ + init_sets(); + + set_command(&command, CMD_RESTORE); + break; + } + + case 'A':{ /* Add IP */ + init_sets(); + + set_command(&command, CMD_ADD); + + set = set_find_byname(optarg); + + if (set_level >= set->levels) + exit_error(PARAMETER_PROBLEM, + "specified childset is deeper than " + "%s set itself.", set->name); + + /* entries */ + if (optind < argc + && argv[optind][0] != '-') + entries = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires setname and entries", + cmd2char(CMD_ADD)); + + break; + } + + case 'D':{ /* Del IP */ + init_sets(); + + set_command(&command, CMD_DEL); + + set = set_find_byname(optarg); + + if (set_level >= set->levels) + exit_error(PARAMETER_PROBLEM, + "specified childset is deeper than " + "%s set itself.", set->name); + + if (optind < argc + && argv[optind][0] != '-') + entries = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires setname and entries", + cmd2char(CMD_DEL)); + + break; + } + + case 'T':{ /* Test IP */ + init_sets(); + + set_command(&command, CMD_TEST); + + set = set_find_byname(optarg); + name = optarg; + + if (set_level >= set->levels) + exit_error(PARAMETER_PROBLEM, + "specified childset is deeper than " + "%s set itself.", set->name); + + if (optind < argc + && argv[optind][0] != '-') + entries = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires setname and entries", + cmd2char(CMD_TEST)); + + 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 IP_SET_DEBUG + case 'z': /* debug */ + add_option(&options, OPT_DEBUG); + option_debug = 1; + break; +#endif + + case 'c': + add_option(&options, OPT_CHILDSETS); + break; + + case 'i': + add_option(&options, OPT_HINT); + 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 = set->settype[set_level]->create_parse( + c - set->settype[set_level]->option_offset, + argv, + set->settype[set_level]->data, + &set->settype[set_level]->flags); + break; + + case CMD_CREATE_CHILD: + res = set->settype[set_level]->create_parse( + c - set->settype[set_level]->option_offset, + argv, + set->settype[set_level]->data, + &set->settype[set_level]->flags); + break; + + case CMD_HELP: { + if (!(settype && settype->hint_parse)) + break; + + res = settype->hint_parse( + c - settype->option_offset, + argv, + settype->data); + 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) + exit_error(PARAMETER_PROBLEM, "no command specified"); + + /* Check options */ + generic_opt_check(command == CMD_CREATE_CHILD ? CMD_CREATE : command, options); + + DP("cmd: %c", cmd2char(command)); + + switch (command) { + case CMD_CREATE: + DP("CMD_CREATE"); + set_create(set); + break; + + case CMD_CREATE_CHILD: + DP("CMD_CREATE_CHILD"); + set_create_childset(set, options); + break; + + case CMD_DESTROY: + set_destroy(set); + break; + + case CMD_FLUSH: + set_flush(set, options); + break; + + case CMD_RENAME: + set_rename(set, name); + break; + + case CMD_SWAP: + set_swap(set, set_to); + break; + + case CMD_LIST: + set_list(set, options); + break; + + case CMD_SAVE: + set_save(set); + break; + + case CMD_RESTORE: + set_restore(); + break; + + case CMD_ADD: + set_addip(set, entries); + break; + + case CMD_DEL: + set_delip(set, entries, options); + break; + + case CMD_TEST: + res = set_testip(set, name, entries); + break; + + case CMD_HELP: + if (options & OPT_HINT) + res = settype_hint(settype, options); + else + set_help(settype); + break; + + default: + /* Will never happen */ + ; /* Keep the compiler happy */ + + } /* switch( command ) */ + + return res; +} + + +int main(int argc, char *argv[]) +{ + return parse_commandline(argc, argv, 0); + +} @@ -0,0 +1,203 @@ +#ifndef __IPSET_H +#define __IPSET_H + +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <getopt.h> +#include <sys/types.h> +#include <netdb.h> + +#include <linux/netfilter_ipv4/ip_set.h> + +char program_name[] = "ipset"; +char program_version[] = "1.0"; + +#define IPSET_LIB_NAME "/libipset_%s.so" +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" + +#define LIST_TRIES 5 + +/* FIXME: move this to Makefile ? */ +#if 1 +#define IP_SET_DEBUG +#endif + +#ifdef IP_SET_DEBUG +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 + +enum exittype { + OTHER_PROBLEM = 1, + PARAMETER_PROBLEM, + VERSION_PROBLEM +}; + +struct set_data { + void *setdata; /* Hook to set speficic data */ + void *bitmap; /* Which elements has got a childset (bitmap) */ + void *adt; /* Add/del/test data */ +}; + +/* The simplistic view of an ipset */ +struct set { + struct set *next; + + char name[IP_SET_MAXNAMELEN]; /* Name of the set */ + unsigned id; /* Pool id in kernel */ + unsigned levels; /* Levels we may have in this set */ + unsigned ref; /* References in kernel */ + struct settype *settype[IP_SET_LEVELS]; /* Pointer to set type functions */ + struct set_data private[IP_SET_LEVELS]; /* Hook to set specific data */ +}; + +#define ADT_ADD 0 +#define ADT_DEL 1 +#define ADT_TEST 2 + +struct settype { + struct settype *next; + + char typename[IP_SET_MAXNAMELEN]; + char typecode; + + 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 req_size; + + /* Function which parses command options */ + ip_set_ip_t (*adt_parser) (int cmd, const char *option, + void *data, const void * setdata); + + /* + * Printing + */ + + /* Set up the sets headerinfo with data coming from kernel */ + void (*initheader) (void ** setdata, void *data, size_t len); + + /* Set up the sets members with data coming from kernel */ + void (*initmembers) (void * setdata, void *data, size_t len); + + /* Remove the members memory usage */ + void (*killmembers) (void ** setdata); + + /* Pretty print the type-header */ + void (*printheader) (const void * setdata, unsigned options); + + /* Returns an IP address from the set */ + ip_set_ip_t (*getipbyid) (const void * setdata, ip_set_ip_t id); + + /* Return the size of the set in ids */ + ip_set_ip_t (*sizeid) (const void * setdata); + + /* Pretty print all IPs */ + void (*printips) (const void * setdata, unsigned options); + + /* Pretty print all IPs sorted */ + void (*printips_sorted) (const void * setdata, + unsigned options); + + /* Print save arguments for creating the set */ + void (*saveheader) (const void * setdata); + + /* Print save for all IPs */ + void (*saveips) (const void * setdata); + + /* Print usage */ + void (*usage) (void); + + /* + * Hint: size of data must be smaller than that of create! + */ + + /* Size of hint data. Will *not* be sent to kernel */ + size_t hint_size; + + /* Initialize the hint data. */ + void (*hint_init) (void *data); + + /* Function which parses command options; returns true if it ate an option */ + int (*hint_parse) (int c, char *argv[], void *data); + + /* Pointer to list of extra command-line options for hinting */ + struct option *hint_opts; + + /* Hint initialization info */ + void (*hint) (const void *data, ip_set_ip_t * ip, ip_set_ip_t id); + + /* Internal data */ + unsigned int option_offset; + unsigned int flags; + void *data; +}; + +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 *ip_tostring(ip_set_ip_t ip, unsigned options); +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 void ipset_free(void **data); + +#endif /* __IPSET_H */ diff --git a/ipset_iphash.c b/ipset_iphash.c new file mode 100644 index 0000000..ea18296 --- /dev/null +++ b/ipset_iphash.c @@ -0,0 +1,477 @@ +/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <asm/bitops.h> +#include <asm/types.h> + +#include <linux/netfilter_ipv4/ip_set_iphash.h> +#include <linux/netfilter_ipv4/ip_set_jhash.h> + +#include "ipset.h" + +#define BUFLEN 30; + +#define OPT_CREATE_INITVAL 0x01U +#define OPT_CREATE_HASHSIZE 0x02U +#define OPT_CREATE_NETMASK 0x03U + +#define OPT_HINT_TRY 0x01U +#define OPT_HINT_NETMASK 0x02U + +#define HINT_DEFAULT_TRY 8 +#define HINT_DEFAULT_FACTOR 4 + +/* 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"); + + mydata->initval = 0; + 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; + char *end; + unsigned int bits; + + DP("create_parse"); + + switch (c) { + case '1': + errno = 0; + mydata->initval = strtoul(optarg, &end, 0); + if (*end == '\0' && end != optarg && errno != ERANGE) { + + *flags |= OPT_CREATE_INITVAL; + + DP("--initval 0x%x)", mydata->initval); + break; + } + exit_error(PARAMETER_PROBLEM, "Invalid initval `%s' specified", optarg); + case '2': + + if (string_to_number(optarg, 1, MAX_RANGE, &mydata->hashsize)) + exit_error(PARAMETER_PROBLEM, "Hashsize `%s' specified", optarg); + + *flags |= OPT_CREATE_HASHSIZE; + + DP("--hashsize %u", mydata->hashsize); + + break; + + case '3': + + if (string_to_number(optarg, 0, 32, &bits)) + exit_error(PARAMETER_PROBLEM, + "Invalid netmask `%s' specified", optarg); + + if (bits != 0) + mydata->netmask = 0xFFFFFFFF << (32 - bits); + + *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) +{ + struct ip_set_req_iphash_create *mydata = + (struct ip_set_req_iphash_create *) data; + + if ((flags & OPT_CREATE_HASHSIZE) == 0) + exit_error(PARAMETER_PROBLEM, + "Need to specify --hashsize\n"); + + if (!mydata->initval) { + srand(getpid() | time(NULL)); + mydata->initval = rand(); + } + DP("initval: 0x%x hashsize %d", mydata->initval, mydata->hashsize); +} + +/* Create commandline options */ +static struct option create_opts[] = { + {"initval", 1, 0, '1'}, + {"hashsize", 1, 0, '2'}, + {"netmask", 1, 0, '3'}, + {0} +}; + +/* Add, del, test parser */ +ip_set_ip_t adt_parser(int cmd, const char *optarg, + void *data, const void *setdata) +{ + struct ip_set_req_iphash *mydata = + (struct ip_set_req_iphash *) data; + + mydata->flags = 0; + + if (*optarg == '+') { + if (cmd == ADT_ADD) { + mydata->flags |= IPSET_ADD_OVERWRITE; + optarg++; + } else + exit_error(PARAMETER_PROBLEM, + "The '!' overwrite flag can be used only " + "when adding an IP address to the set\n"); + } + parse_ip(optarg, &mydata->ip); + + return mydata->ip; +}; + +ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id) +{ + struct ip_set_iphash *mysetdata = + (struct ip_set_iphash *) setdata; + + return mysetdata->members[id]; +} + +ip_set_ip_t sizeid(const void *setdata) +{ + struct ip_set_iphash *mysetdata = + (struct ip_set_iphash *) setdata; + + return (mysetdata->hashsize); +} + +void initheader(void **setdata, void *data, size_t len) +{ + struct ip_set_req_iphash_create *header = + (struct ip_set_req_iphash_create *) data; + + DP("iphash: initheader() 1"); + + if (len != sizeof(struct ip_set_req_iphash_create)) + exit_error(OTHER_PROBLEM, + "Iphash: incorrect size of header. " + "Got %d, wanted %d.", len, + sizeof(struct ip_set_req_iphash_create)); + + *setdata = ipset_malloc(sizeof(struct ip_set_iphash)); + + DP("iphash: initheader() 2"); + + ((struct ip_set_iphash *) *setdata)->initval = + header->initval; + ((struct ip_set_iphash *) *setdata)->hashsize = + header->hashsize; + ((struct ip_set_iphash *) *setdata)->netmask = + header->netmask; +} + +void initmembers(void *setdata, void *data, size_t len) +{ + struct ip_set_iphash *mysetdata = + (struct ip_set_iphash *) setdata; + size_t size; + + DP("iphash: initmembers()"); + + /* Check so we get the right amount of memberdata */ + size = sizeof(ip_set_ip_t) * mysetdata->hashsize; + + if (len != size) + exit_error(OTHER_PROBLEM, + "Iphash: incorrect size of members. " + "Got %d, wanted %d.", len, size); + + mysetdata->members = data; +} + +void killmembers(void **setdata) +{ + struct ip_set_iphash *mysetdata = + (struct ip_set_iphash *) *setdata; + + DP("iphash: killmembers()"); + + if (mysetdata->members != NULL) + free(mysetdata->members); + + ipset_free(setdata); +} + +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(const void *setdata, unsigned options) +{ + struct ip_set_iphash *mysetdata = + (struct ip_set_iphash *) setdata; + + printf(" initval: 0x%x", mysetdata->initval); + printf(" hashsize: %d", mysetdata->hashsize); + if (mysetdata->netmask == 0xFFFFFFFF) + printf("\n"); + else + printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask)); +} + +void printips(const void *setdata, unsigned options) +{ + struct ip_set_iphash *mysetdata = + (struct ip_set_iphash *) setdata; + ip_set_ip_t id; + + for (id = 0; id < mysetdata->hashsize; id++) + if (mysetdata->members[id]) + printf("%s\n", ip_tostring(mysetdata->members[id], options)); +} + +void saveheader(const void *setdata) +{ + return; +} + +/* Print save for an IP */ +void saveips(const void *setdata) +{ + return; +} + +void usage(void) +{ + printf + ("-N set iphash [--initval hash-initval] --hashsize hashsize [--netmask CIDR-netmask]\n" + "-A set [!]IP\n" + "-D set IP\n" + "-T set IP\n" + "-H iphash -i [--try number] [--factor number] [--netmask CIDR-netmask]\n"); +} + +struct ip_set_iphash_hint { + unsigned int try; + unsigned int factor; + ip_set_ip_t netmask; +}; + +/* Initialize the hint. */ +void hint_init(void *data) +{ + struct ip_set_iphash_hint *mydata = + (struct ip_set_iphash_hint *) data; + + DP("hint INIT"); + + mydata->try = HINT_DEFAULT_TRY; + mydata->factor = HINT_DEFAULT_FACTOR; + mydata->netmask = 0xFFFFFFFF; +} + +/* Function which parses command options; returns true if it ate an option */ +int hint_parse(int c, char *argv[], void *data) +{ + struct ip_set_iphash_hint *mydata = + (struct ip_set_iphash_hint *) data; + unsigned int bits; + + DP("hint_parse"); + + switch (c) { + case '1': + if (string_to_number(optarg, 1, 32, &mydata->try)) + exit_error(PARAMETER_PROBLEM, + "Invalid --try `%s' specified (out of range 1-32", optarg); + + DP("--try %i)", mydata->try); + break; + + case '2': + if (string_to_number(optarg, 1, 64, &mydata->factor)) + exit_error(PARAMETER_PROBLEM, + "Invalid --factor `%s' specified (out of range 1-64", optarg); + + DP("--factor %i)", mydata->factor); + break; + + case '3': + + if (string_to_number(optarg, 0, 32, &bits)) + exit_error(PARAMETER_PROBLEM, + "Invalid netmask `%s' specified", optarg); + + if (bits != 0) + mydata->netmask = 0xFFFFFFFF << (32 - bits); + + DP("--netmask %x", mydata->netmask); + + break; + + default: + return 0; + } + + return 1; +} + +/* Hint commandline options */ +static struct option hint_opts[] = { + {"try", 1, 0, '1'}, + {"factor", 1, 0, '2'}, + {"netmask", 1, 0, '3'}, + {0} +}; + +int hint_try(const ip_set_ip_t *ip, ip_set_ip_t *test, ip_set_ip_t best, + ip_set_ip_t hashsize, unsigned int try, uint32_t *initval, int *found) +{ + ip_set_ip_t id, hash; + int i = 0; + + next_try: + while (i < try) { + memset(test, 0, MAX_RANGE * sizeof(ip_set_ip_t)); + for (id = 0; id < best; id++) { + hash = jhash_1word(ip[id], *initval) % hashsize; + if (test[hash] != 0) { + *initval = rand(); + i++; + goto next_try; + } else + test[hash] = ip[id]; + DP("%i %x", hash, ip[id]); + } + printf("--initval 0x%08x --hashsize %d\n", *initval, hashsize); + *found = 1; + return 1; + } + return 0; +} + +void hint(const void *data, ip_set_ip_t *ip, ip_set_ip_t best) +{ + struct ip_set_iphash_hint *mydata = + (struct ip_set_iphash_hint *) data; + uint32_t initval; + ip_set_ip_t next, prev, curr; + ip_set_ip_t test[MAX_RANGE]; + ip_set_ip_t id; + int found = 0; + + curr = MAX_RANGE > mydata->factor * best ? mydata->factor * best : MAX_RANGE; + prev = next = MAX_RANGE; + + srand(curr); + initval = rand(); + + for (id = 0; id < best; id++) + ip[id] &= mydata->netmask; + + while (1) { + DP("%u %u %u %u", prev, curr, next, best); + + if (hint_try(ip, test, best, curr, mydata->try, &initval, &found)) { + if (curr == best) + return; + next = (curr + best)/2; + } else { + if (curr == prev){ + if (!found) + printf("Cannot find good init values.\n"); + return; + } + next = (curr + prev)/2; + } + prev = curr; + curr = next; + } +} +static struct settype settype_iphash = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_IP, + .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 */ + .req_size = sizeof(struct ip_set_req_iphash), + .adt_parser = &adt_parser, + + /* Get an IP address by id */ + .getipbyid = &getipbyid, + .sizeid = &sizeid, + + /* Printing */ + .initheader = &initheader, + .initmembers = &initmembers, + .killmembers = &killmembers, + .printheader = &printheader, + .printips = &printips, /* We only have the unsorted version */ + .printips_sorted = &printips, + .saveheader = &saveheader, + .saveips = &saveips, + .usage = &usage, + + /* Hint */ + .hint_size = sizeof(struct ip_set_iphash_hint), + .hint_init = &hint_init, + .hint_parse = &hint_parse, + .hint_opts = hint_opts, + .hint = &hint, +}; + +void _init(void) +{ + settype_register(&settype_iphash); + +} diff --git a/ipset_ipmap.c b/ipset_ipmap.c new file mode 100644 index 0000000..df03a14 --- /dev/null +++ b/ipset_ipmap.c @@ -0,0 +1,384 @@ +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <strings.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <asm/bitops.h> + +#include <linux/netfilter_ipv4/ip_set_ipmap.h> +#include "ipset.h" + +#define BUFLEN 30; + +#define OPT_CREATE_FROM 0x01U +#define OPT_CREATE_TO 0x02U +#define OPT_CREATE_NETWORK 0x04U +#define OPT_CREATE_NETMASK 0x08U + +#define OPT_ADDDEL_IP 0x01U + +/* Initialize the create. */ +void create_init(void *data) +{ + struct ip_set_req_ipmap_create *mydata = + (struct ip_set_req_ipmap_create *) data; + + DP("create INIT"); + mydata->netmask = 0xFFFFFFFF; +} + +/* Function which parses command options; returns true if it ate an option */ +int create_parse(int c, char *argv[], void *data, unsigned *flags) +{ + struct ip_set_req_ipmap_create *mydata = + (struct ip_set_req_ipmap_create *) data; + unsigned int bits; + + DP("create_parse"); + + switch (c) { + case '1': + parse_ip(optarg, &mydata->from); + + *flags |= OPT_CREATE_FROM; + + DP("--from %x (%s)", mydata->from, + ip_tostring(mydata->from, 0)); + + break; + + case '2': + parse_ip(optarg, &mydata->to); + + *flags |= OPT_CREATE_TO; + + DP("--to %x (%s)", mydata->to, ip_tostring(mydata->to, 0)); + + 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(mydata->from, 0)); + DP("--network to %x (%s)", mydata->to, + ip_tostring(mydata->to, 0)); + + 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) +{ + struct ip_set_req_ipmap_create *mydata = + (struct ip_set_req_ipmap_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", 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", MAX_RANGE); + + if (mydata->to - mydata->from > MAX_RANGE) + exit_error(PARAMETER_PROBLEM, + "Range to large. Max is %d IPs in range\n", + MAX_RANGE); + 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(mydata->from, 0), + mask_to_bits(mydata->netmask)); + + mask = range_to_mask(mydata->from, mydata->to, &mask_bits); + if (!mask) + exit_error(PARAMETER_PROBLEM, + "%s-%s is not a full network\n", + ip_tostring(mydata->from, 0), + ip_tostring(mydata->to, 0)); + + netmask_bits = mask_to_bits(mydata->netmask); + + if (netmask_bits <= mask_bits) + exit_error(PARAMETER_PROBLEM, + "%d netmask specifies larger or equal netblock than %s-%s\n", + netmask_bits, + ip_tostring(mydata->from, 0), + ip_tostring(mydata->to, 0)); + } +} + +/* 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(int cmd, const char *optarg, + void *data, const void *setdata) +{ + struct ip_set_req_ipmap *mydata = + (struct ip_set_req_ipmap *) data; + struct ip_set_ipmap *mysetdata = + (struct ip_set_ipmap *) setdata; + + DP("ipmap: %p %p", data, setdata); + + parse_ip(optarg, &mydata->ip); + + DP("from %s", ip_tostring(mysetdata->first_ip, 0)); + DP("to %s", ip_tostring(mysetdata->last_ip, 0)); + DP("ip %s", ip_tostring(mydata->ip, 0)); + + if (mydata->ip < mysetdata->first_ip || + mydata->ip > mysetdata->last_ip) + exit_error(PARAMETER_PROBLEM, "IP '%s' is out of range\n", + ip_tostring(mydata->ip, 0)); + + return mydata->ip; +} + +ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id) +{ + struct ip_set_ipmap *mysetdata = + (struct ip_set_ipmap *) setdata; + + return (mysetdata->first_ip + id * mysetdata->hosts); +} + +ip_set_ip_t sizeid(const void *setdata) +{ + struct ip_set_ipmap *mysetdata = + (struct ip_set_ipmap *) setdata; + + return (mysetdata->sizeid); +} + +void initheader(void **setdata, void *data, size_t len) +{ + struct ip_set_req_ipmap_create *header = + (struct ip_set_req_ipmap_create *) data; + struct ip_set_ipmap *map; + + DP("ipmap: initheader() 1"); + + if (len != sizeof(struct ip_set_req_ipmap_create)) + exit_error(OTHER_PROBLEM, + "Ipmap: incorrect size of header. " + "Got %d, wanted %d.", len, + sizeof(struct ip_set_req_ipmap_create)); + + *setdata = ipset_malloc(sizeof(struct ip_set_ipmap)); + + DP("ipmap: initheader() 2"); + + map = (struct ip_set_ipmap *) *setdata; + + 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 initmembers(void *setdata, void *data, size_t len) +{ + struct ip_set_ipmap *mysetdata = + (struct ip_set_ipmap *) setdata; + size_t size; + + DP("ipmap: initmembers()"); + + /* Check so we get the right amount of memberdata */ + size = bitmap_bytes(0, mysetdata->sizeid - 1); + + if (len != size) + exit_error(OTHER_PROBLEM, + "Ipmap: incorrect size of members. " + "Got %d, wanted %d.", len, size); + + mysetdata->members = data; +} + +void killmembers(void **setdata) +{ + struct ip_set_ipmap *mysetdata = + (struct ip_set_ipmap *) *setdata; + + DP("ipmap: killmembers()"); + + if (mysetdata->members != NULL) + ipset_free(&mysetdata->members); + + ipset_free(setdata); +} + +void printheader(const void *setdata, unsigned options) +{ + struct ip_set_ipmap *mysetdata = + (struct ip_set_ipmap *) setdata; + + 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(const void *setdata, unsigned options) +{ + struct ip_set_ipmap *mysetdata = + (struct ip_set_ipmap *) setdata; + ip_set_ip_t id; + + for (id = 0; id < mysetdata->sizeid; id++) + if (test_bit(id, mysetdata->members)) + printf("%s\n", ip_tostring(mysetdata->first_ip + id * mysetdata->hosts, + options)); +} + +void saveheader(const void *setdata) +{ + return; +} + +/* Print save for an IP */ +void saveips(const void *setdata) +{ + return; +} + +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, + .typecode = IPSET_TYPE_IP, + .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 */ + .req_size = sizeof(struct ip_set_req_ipmap), + .adt_parser = &adt_parser, + + /* Get an IP address by id */ + .getipbyid = &getipbyid, + .sizeid = &sizeid, + + /* Printing */ + .initheader = &initheader, + .initmembers = &initmembers, + .killmembers = &killmembers, + .printheader = &printheader, + .printips = &printips_sorted, /* We only have sorted version */ + .printips_sorted = &printips_sorted, + .saveheader = &saveheader, + .saveips = &saveips, + .usage = &usage, + .hint = NULL, +}; + +void _init(void) +{ + settype_register(&settype_ipmap); + +} diff --git a/ipset_macipmap.c b/ipset_macipmap.c new file mode 100644 index 0000000..eff42ce --- /dev/null +++ b/ipset_macipmap.c @@ -0,0 +1,387 @@ +/* Copyright 2000, 2001, 2002 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Martin Josefsson (gandalf@wlug.westbo.se) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <asm/bitops.h> +#include <linux/if_ether.h> + +#include <linux/netfilter_ipv4/ip_set_macipmap.h> +#include "ipset.h" + +#define BUFLEN 30; + +#define OPT_CREATE_FROM 0x01U +#define OPT_CREATE_TO 0x02U +#define OPT_CREATE_NETWORK 0x04U +#define OPT_CREATE_MATCHUNSET 0x08U + +#define OPT_ADDDEL_IP 0x01U +#define OPT_ADDDEL_MAC 0x02U + +/* Initialize the create. */ +void create_init(void *data) +{ + DP("create INIT"); + /* Nothing */ +} + +/* Function which parses command options; returns true if it ate an option */ +int create_parse(int c, char *argv[], void *data, unsigned *flags) +{ + struct ip_set_req_macipmap_create *mydata = + (struct ip_set_req_macipmap_create *) data; + + DP("create_parse"); + + switch (c) { + case '1': + parse_ip(optarg, &mydata->from); + + *flags |= OPT_CREATE_FROM; + + DP("--from %x (%s)", mydata->from, + ip_tostring(mydata->from, 0)); + + break; + + case '2': + parse_ip(optarg, &mydata->to); + + *flags |= OPT_CREATE_TO; + + DP("--to %x (%s)", mydata->to, ip_tostring(mydata->to, 0)); + + 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(mydata->from, 0)); + DP("--network to %x (%s)", mydata->to, + ip_tostring(mydata->to, 0)); + + 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", MAX_RANGE); + + if (mydata->to - mydata->from > MAX_RANGE) + exit_error(PARAMETER_PROBLEM, + "Range to large. Max is %d IPs in range\n", + MAX_RANGE); +} + +/* 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(int cmd, const char *optarg, + void *data, const void *setdata) +{ + struct ip_set_req_macipmap *mydata = + (struct ip_set_req_macipmap *) data; + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) setdata; + char *saved = strdup(optarg); + char *ptr, *tmp = saved; + + DP("macipmap: %p %p", data, setdata); + + ptr = strsep(&tmp, "%"); + parse_ip(ptr, &mydata->ip); + + if (cmd == ADT_ADD && !tmp) + exit_error(PARAMETER_PROBLEM, + "Need to specify ip[mac]\n"); + + if (tmp) + parse_mac(tmp, mydata->ethernet); + else + memset(mydata->ethernet, 0, ETH_ALEN); + + DP("from %s", ip_tostring(mysetdata->first_ip, 0)); + DP("to %s", ip_tostring(mysetdata->last_ip, 0)); + DP("ip %s", ip_tostring(mydata->ip, 0)); + + if (mydata->ip < mysetdata->first_ip || + mydata->ip > mysetdata->last_ip) + exit_error(PARAMETER_PROBLEM, "IP '%s' is out of range\n", + ip_tostring(mydata->ip, 0)); + + free(saved); + + return mydata->ip; +} + +ip_set_ip_t getipbyid(const void *setdata, ip_set_ip_t id) +{ + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) setdata; + + return (mysetdata->first_ip + id); +} + +ip_set_ip_t sizeid(const void *setdata) +{ + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) setdata; + + return (mysetdata->last_ip - mysetdata->first_ip + 1); +} + +void initheader(void **setdata, void *data, size_t len) +{ + struct ip_set_req_macipmap_create *header = + (struct ip_set_req_macipmap_create *) data; + + DP("macipmap: initheader() 1"); + + if (len != sizeof(struct ip_set_req_macipmap_create)) + exit_error(OTHER_PROBLEM, + "Macipmap: incorrect size of header. " + "Got %d, wanted %d.", len, + sizeof(struct ip_set_req_macipmap_create)); + + *setdata = ipset_malloc(sizeof(struct ip_set_macipmap)); + + DP("bitmap: initheader() 2"); + + ((struct ip_set_macipmap *) *setdata)->first_ip = header->from; + ((struct ip_set_macipmap *) *setdata)->last_ip = header->to; + ((struct ip_set_macipmap *) *setdata)->flags = header->flags; +} + +void initmembers(void *setdata, void *data, size_t len) +{ + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) setdata; + size_t size; + + DP("macipmap: initmembers()"); + + /* Check so we get the right amount of memberdata */ + size = (mysetdata->last_ip - mysetdata->first_ip) + * sizeof(struct ip_set_macip); + + if (len != size) + exit_error(OTHER_PROBLEM, + "Macipmap: incorrect size of members. " + "Got %d, wanted %d.", len, size); + + mysetdata->members = data; +} + +void killmembers(void **setdata) +{ + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) *setdata; + + DP("macipmap: killmembers()"); + + if (mysetdata->members != NULL) + ipset_free(&mysetdata->members); + + ipset_free(setdata); +} + +void printheader(const void *setdata, unsigned options) +{ + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) setdata; + + 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(const void *setdata, unsigned options) +{ + struct ip_set_macipmap *mysetdata = + (struct ip_set_macipmap *) setdata; + struct ip_set_macip *table = + (struct ip_set_macip *) mysetdata->members; + u_int32_t addr = mysetdata->first_ip; + + while (addr <= mysetdata->last_ip) { + if (test_bit(IPSET_MACIP_ISSET, + &table[addr - mysetdata->first_ip].flags)) { + printf("%s%s", ip_tostring(addr, options), "%"); + print_mac(table[addr - mysetdata->first_ip]. + ethernet); + printf("\n"); + } + addr++; + } +} + +void saveheader(const void *setdata) +{ + return; +} + +/* Print save for an IP */ +void saveips(const void *setdata) +{ + return; +} + +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, + .typecode = IPSET_TYPE_IP, + .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 */ + .req_size = sizeof(struct ip_set_req_macipmap), + .adt_parser = &adt_parser, + + /* Get an IP address by id */ + .getipbyid = &getipbyid, + .sizeid = &sizeid, + + /* Printing */ + .initheader = &initheader, + .initmembers = &initmembers, + .killmembers = &killmembers, + .printheader = &printheader, + .printips = &printips_sorted, /* We only have sorted version */ + .printips_sorted = &printips_sorted, + .saveheader = &saveheader, + .saveips = &saveips, + .usage = &usage, + .hint = NULL, +}; + +void _init(void) +{ + settype_register(&settype_macipmap); + +} diff --git a/ipset_portmap.c b/ipset_portmap.c new file mode 100644 index 0000000..fd8746f --- /dev/null +++ b/ipset_portmap.c @@ -0,0 +1,293 @@ +/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <asm/bitops.h> + +#include <linux/netfilter_ipv4/ip_set_portmap.h> +#include "ipset.h" + + +#define BUFLEN 30; + +#define OPT_CREATE_FROM 0x01U +#define OPT_CREATE_TO 0x02U + +#define OPT_ADDDEL_PORT 0x01U + +/* Initialize the create. */ +void create_init(void *data) +{ + DP("create INIT"); + /* Nothing */ +} + +/* Function which parses command options; returns true if it ate an option */ +int create_parse(int c, char *argv[], void *data, unsigned *flags) +{ + struct ip_set_req_portmap_create *mydata = + (struct ip_set_req_portmap_create *) data; + + DP("create_parse"); + + switch (c) { + case '1': + parse_port(optarg, &mydata->from); + + *flags |= OPT_CREATE_FROM; + + DP("--from %x (%s)", mydata->from, + port_tostring(mydata->from, 0)); + + break; + + case '2': + parse_port(optarg, &mydata->to); + + *flags |= OPT_CREATE_TO; + + DP("--to %x (%s)", mydata->to, + port_tostring(mydata->to, 0)); + + break; + + default: + return 0; + } + + return 1; +} + +/* Final check; exit if not ok. */ +void create_final(void *data, unsigned int flags) +{ + struct ip_set_req_portmap_create *mydata = + (struct ip_set_req_portmap_create *) data; + + if (flags == 0) { + exit_error(PARAMETER_PROBLEM, + "Need to specify --from and --to\n"); + } else { + /* --from --to */ + if ((flags & OPT_CREATE_FROM) == 0 + || (flags & OPT_CREATE_TO) == 0) + exit_error(PARAMETER_PROBLEM, + "Need to specify both --from and --to\n"); + } + + DP("from : %x to: %x diff: %d", mydata->from, mydata->to, + mydata->to - mydata->from); + + if (mydata->from > mydata->to) + exit_error(PARAMETER_PROBLEM, + "From can't be lower than to.\n", MAX_RANGE); + + if (mydata->to - mydata->from > MAX_RANGE) + exit_error(PARAMETER_PROBLEM, + "Range to large. Max is %d ports in range\n", + MAX_RANGE); +} + +/* 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(int cmd, const char *optarg, + void *data, const void *setdata) +{ + struct ip_set_req_portmap *mydata = + (struct ip_set_req_portmap *) data; + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) setdata; + + DP("portmap: %p %p", data, setdata); + + parse_port(optarg, &mydata->port); + + DP("from %s", port_tostring(mysetdata->first_port, 0)); + DP("to %s", port_tostring(mysetdata->last_port, 0)); + DP("port %s", port_tostring(mydata->port, 0)); + + if (mydata->port < mysetdata->first_port || + mydata->port > mysetdata->last_port) + exit_error(PARAMETER_PROBLEM, "Port '%s' is out of range\n", + port_tostring(mydata->port, 0)); + + return mydata->port; +} + +ip_set_ip_t getportbyid(const void *setdata, ip_set_ip_t id) +{ + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) setdata; + + return (mysetdata->first_port + id); +} + +ip_set_ip_t sizeid(const void *setdata) +{ + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) setdata; + + return (mysetdata->last_port - mysetdata->first_port + 1); +} + +void initheader(void **setdata, void *data, size_t len) +{ + struct ip_set_req_portmap_create *header = + (struct ip_set_req_portmap_create *) data; + + DP("portmap: initheader() 1"); + + if (len != sizeof(struct ip_set_req_portmap_create)) + exit_error(OTHER_PROBLEM, + "Portmap: incorrect size of header. " + "Got %d, wanted %d.", len, + sizeof(struct ip_set_req_portmap_create)); + + *setdata = ipset_malloc(sizeof(struct ip_set_portmap)); + + DP("portmap: initheader() 2"); + + ((struct ip_set_portmap *) *setdata)->first_port = + header->from; + ((struct ip_set_portmap *) *setdata)->last_port = header->to; + +} + +void initmembers(void *setdata, void *data, size_t len) +{ + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) setdata; + size_t size; + + DP("portmap: initmembers()"); + + /* Check so we get the right amount of memberdata */ + size = bitmap_bytes(mysetdata->first_port, mysetdata->last_port); + + if (len != size) + exit_error(OTHER_PROBLEM, + "Portmap: incorrect size of members. " + "Got %d, wanted %d.", len, size); + + mysetdata->members = data; +} + +void killmembers(void **setdata) +{ + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) *setdata; + + DP("portmap: killmembers()"); + + if (mysetdata->members != NULL) + ipset_free(&mysetdata->members); + + ipset_free(setdata); +} + + +void printheader(const void *setdata, unsigned options) +{ + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) setdata; + + printf(" from: %s", port_tostring(mysetdata->first_port, options)); + printf(" to: %s\n", port_tostring(mysetdata->last_port, options)); +} + +void printports_sorted(const void *setdata, unsigned options) +{ + struct ip_set_portmap *mysetdata = + (struct ip_set_portmap *) setdata; + + u_int32_t addr = mysetdata->first_port; + + while (addr <= mysetdata->last_port) { + if (test_bit(addr - mysetdata->first_port, mysetdata->members)) + printf("%s\n", port_tostring(addr, options)); + addr++; + } +} + +void saveheader(const void *setdata) +{ + return; +} + +/* Print save for an IP */ +void saveports(const void *setdata) +{ + return; +} + +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, + .typecode = IPSET_TYPE_PORT, + .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 */ + .req_size = sizeof(struct ip_set_req_portmap), + .adt_parser = &adt_parser, + + /* Get an IP address by id */ + .getipbyid = &getportbyid, + .sizeid = &sizeid, + + /* Printing */ + .initheader = &initheader, + .initmembers = &initmembers, + .killmembers = &killmembers, + .printheader = &printheader, + .printips = &printports_sorted, /* We only have sorted version */ + .printips_sorted = &printports_sorted, + .saveheader = &saveheader, + .saveips = &saveports, + .usage = &usage, + .hint = NULL, +}; + +void _init(void) +{ + settype_register(&settype_portmap); + +} diff --git a/libipt_set.h b/libipt_set.h new file mode 100644 index 0000000..8180f7c --- /dev/null +++ b/libipt_set.h @@ -0,0 +1,132 @@ +#ifndef _LIBIPT_SET_H +#define _LIBIPT_SET_H + +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> + +static int get_set_getsockopt(void *data, size_t * size) +{ + int sockfd = -1; + sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (sockfd < 0) + exit_error(OTHER_PROBLEM, + "Can't open socket to ipset.\n"); + /* Send! */ + return getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size); +} + +static void get_set_byname(const char *setname, struct ipt_set_info *info) +{ + struct ip_set_req_get req; + int size = sizeof(struct ip_set_req_get); + int res; + + req.op = IP_SET_OP_GETSET_BYNAME; + strncpy(req.name, setname, IP_SET_MAXNAMELEN); + req.name[IP_SET_MAXNAMELEN - 1] = '\0'; + res = get_set_getsockopt(&req, &size); + if (res != 0) + exit_error(OTHER_PROBLEM, + "Problem when communicating with ipset. errno=%d.\n", + errno); + if (size != sizeof(struct ip_set_req_get)) + exit_error(OTHER_PROBLEM, + "Incorrect return size from kernel during ipset lookup, " + "(want %d, got %d)\n", + sizeof(struct ip_set_req_get), size); + if (req.id < 0) + exit_error(PARAMETER_PROBLEM, + "Set %s doesn't exist.\n", setname); + + info->id = req.id; +} + +static void get_set_byid(char * setname, unsigned id) +{ + struct ip_set_req_get req; + int size = sizeof(struct ip_set_req_get); + int res; + + req.op = IP_SET_OP_GETSET_BYID; + req.id = id; + res = get_set_getsockopt(&req, &size); + if (res != 0) + exit_error(OTHER_PROBLEM, + "Problem when communicating with ipset. errno=%d.\n", + errno); + if (size != sizeof(struct ip_set_req_get)) + exit_error(OTHER_PROBLEM, + "Incorrect return size from kernel during ipset lookup, " + "(want %d, got %d)\n", + sizeof(struct ip_set_req_get), size); + if (req.id < 0) + exit_error(PARAMETER_PROBLEM, + "Set id %i in kernel doesn't exist.\n", id); + + strncpy(setname, req.name, IP_SET_MAXNAMELEN); +} + +static void +parse_pool(const char *optarg, struct ipt_set_info *info) +{ + char *saved = strdup(optarg); + char *ptr, *tmp = saved; + + ptr = strsep(&tmp, ":"); + get_set_byname(ptr, info); + + while (info->set_level < IP_SET_SETIP_LEVELS && tmp) { + ptr = strsep(&tmp, ","); + if (strncmp(ptr, "src", 3) == 0) + info->flags[info->set_level++] |= IPSET_SRC; + else if (strncmp(ptr, "dst", 3) == 0) + info->flags[info->set_level++] |= IPSET_DST; + else + exit_error(PARAMETER_PROBLEM, + "You must spefify (the comma separated list of) 'src' or 'dst'."); + } + + if (tmp || info->set_level >= IP_SET_SETIP_LEVELS) + exit_error(PARAMETER_PROBLEM, + "Defined childset level is deeper that %i.", + IP_SET_SETIP_LEVELS); + + free(saved); +} + +static int +parse_ipflags(const char *optarg, struct ipt_set_info *info) +{ + char *saved = strdup(optarg); + char *ptr, *tmp = saved; + int overwrite = 0; + + info->ip_level = info->set_level; + + while (info->ip_level < IP_SET_LEVELS && tmp) { + if (*tmp == '+') { + info->flags[info->ip_level] |= IPSET_ADD_OVERWRITE; + tmp++; + overwrite++; + } + ptr = strsep(&tmp, ","); + if (strncmp(ptr, "src", 3) == 0) + info->flags[info->ip_level++] |= IPSET_SRC; + else if (strncmp(ptr, "dst", 3) == 0) + info->flags[info->ip_level++] |= IPSET_DST; + else + exit_error(PARAMETER_PROBLEM, + "You must spefify (the comma separated list of) 'src' or 'dst'."); + } + + if (tmp || info->ip_level >= IP_SET_LEVELS) + exit_error(PARAMETER_PROBLEM, + "Defined level is deeper that %i.", + IP_SET_LEVELS); + + free(saved); + return overwrite; +} + +#endif /*_LIBIPT_SET_H*/ |