[netfilter-cvslog] r3305 - in trunk/patch-o-matic-ng/set: .
linux/Documentation linux/include/linux/netfilter_ipv4
linux/net/ipv4/netfilter linux-2.6 linux-2.6/include
linux-2.6/include/linux
linux-2.6/include/linux/netfilter_ipv4 linux-2.6/net
linux-2.6/net/ipv4 linux-2.6/net/ipv4/netfilter
kadlec at netfilter.org
kadlec at netfilter.org
Wed Dec 1 10:49:37 CET 2004
Author: kadlec at netfilter.org
Date: 2004-12-01 10:49:36 +0100 (Wed, 01 Dec 2004)
New Revision: 3305
Added:
trunk/patch-o-matic-ng/set/linux-2.6/
trunk/patch-o-matic-ng/set/linux-2.6/include/
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set.h
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_iphash.h
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_ipmap.h
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_jhash.h
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_macipmap.h
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_portmap.h
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_prime.h
trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ipt_set.h
trunk/patch-o-matic-ng/set/linux-2.6/net/
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Kconfig.ladd
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Makefile.ladd
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Makefile.ladd_2
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set.c
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_iphash.c
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_ipmap.c
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_macipmap.c
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_portmap.c
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ipt_SET.c
trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ipt_set.c
trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_prime.h
Modified:
trunk/patch-o-matic-ng/set/help
trunk/patch-o-matic-ng/set/info
trunk/patch-o-matic-ng/set/linux/Documentation/Configure.help.ladd
trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set.h
trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_iphash.h
trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_ipmap.h
trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_macipmap.h
trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_portmap.h
trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ipt_set.h
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/Config.in.ladd
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/Makefile.ladd
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set.c
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_iphash.c
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_ipmap.c
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_macipmap.c
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_portmap.c
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ipt_SET.c
trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ipt_set.c
Log:
ipset 2 version added (JK)
Modified: trunk/patch-o-matic-ng/set/help
===================================================================
--- trunk/patch-o-matic-ng/set/help 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/help 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,11 +1,9 @@
This adds CONFIG_IP_NF_SET, which provides a match which lets you use
-IP address/network address/port sets; the match depends on whether a
-checked source or destination address/network address/port has its bit
-set in/added to the set. It also provides a SET target, which can be
-used to add or remove the addresses/ports of a packet to/from a set.
-The currently supported types of the sets are: ipmap, macipmap, portmap
-and iphash. It is possible to define multilevel sets up to a hardcoded
-level.
+IP sets; the match depends on whether a checked source or destination
+address/network address/port has its bit set in/added to the given set.
+It also provides a SET target, which can be used to add or remove the
+addresses/ports of a packet to/from a set. The currently supported types
+of the sets are: ipmap, macipmap, portmap and iphash. It is possible
+to chain sets together by binding.
-The userspace program ipset(8) is also compiled (in the ipset/
-directory), and is used to define the sets and their bounds.
+The userspace program ipset(8) is required to define and setup IP sets.
Modified: trunk/patch-o-matic-ng/set/info
===================================================================
--- trunk/patch-o-matic-ng/set/info 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/info 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,4 +1,4 @@
Author: Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
Status: Beta
Repository: base
-Requires: linux < 2.6.0
+Recompile: kernel, netfilter, iptables
Modified: trunk/patch-o-matic-ng/set/linux/Documentation/Configure.help.ladd
===================================================================
--- trunk/patch-o-matic-ng/set/linux/Documentation/Configure.help.ladd 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/Documentation/Configure.help.ladd 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,17 +1,31 @@
CONFIG_IP_NF_TARGET_LOG
-IP address/port set (ipset) support
+IP set support
CONFIG_IP_NF_SET
- This option adds IP address/port set (i.e. ipset) support to the kernel.
- It will enable the `set' match and `SET' target support in netfilter
- as well.
+ This option adds IP set support to the kernel.
- In order to define and use sets, you need userlevel utilities: a patches
- iptables and the program ipset(8), which defines the sets and their
- bounds.
+ In order to define and use sets, you need userlevel utilities: an
+ iptables binary which knows about IP sets and the program ipset(8),
+ by which you can define and setup the sets themselves.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
+set match support
+CONFIG_IP_NF_MATCH_SET
+ This option adds IP set match support.
+ You need the ipset utility to create and set up the sets.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+SET target support
+CONFIG_IP_NF_TARGET_SET
+ This option adds IP set target support.
+ You need the ipset utility to create and set up the sets.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
ipmap set type support
CONFIG_IP_NF_SET_IPMAP
This option adds the ipmap set type support.
Modified: trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,23 +1,14 @@
#ifndef _IP_SET_H
#define _IP_SET_H
-/* Copyright 2000-2004 Joakim Axelsson (gozem at linux.nu)
- * Patrick Schaaf (bof at bof.de)
- * Jozsef Kadlecsik (kadlec at blackhole.kfki.hu)
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Martin Josefsson <gandalf at wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at 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
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
/*
@@ -33,44 +24,22 @@
*/
#define SO_IP_SET 83
-/* Directions: */
-#define IPSET_SRC 0x01
-#define IPSET_DST 0x02
-/* Inverse flag for matching: */
-#define IPSET_MATCH_INV 0x04
-/* Overwrite at adding a new entry: */
-#define IPSET_ADD_OVERWRITE 0x08
-/* Set typecodes: */
-#define IPSET_TYPE_IP 0x10
-#define IPSET_TYPE_PORT 0x20
-
-/*FIXME: remove this */
-/* #define CONFIG_IP_NF_SET_DEBUG */
-
/*
* Heavily modify by Joakim Axelsson 08.03.2002
* - Made it more modulebased
*
* Additional heavy modifications by Jozsef Kadlecsik 22.02.2004
- * - multilevel pools (sets)
+ * - bindings added
* - in order to "deal with" backward compatibility, renamed to ipset
*/
-/* Used so that the kernel module and ipset-binary can match thier versions
+/*
+ * Used so that the kernel module and ipset-binary can match their versions
*/
-#define IP_SET_PROTOCOL_VERSION 1
+#define IP_SET_PROTOCOL_VERSION 2
#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */
-/* The max level of the sets.
- * Do not increase lightheartedly before eliminating
- * the recursive functions from ip_set.c.
- */
-/* So many IPs can identify a set: */
-#define IP_SET_SETIP_LEVELS 4
-/* Max level of a set: */
-#define IP_SET_LEVELS (IP_SET_SETIP_LEVELS+1)
-
/* Lets work with our own typedef for representing an IP address.
* We hope to make the code more portable, possibly to IPv6...
*
@@ -79,9 +48,6 @@
*
* For now the type is an uint32_t.
*
- * We do not enforce, but assume that a set may not store more than
- * 65536 entries.
- *
* Make sure to ONLY use the functions when translating and parsing
* in order to keep the host byte order and make it more portable:
* parse_ip()
@@ -93,133 +59,232 @@
typedef uint32_t ip_set_ip_t;
+/* Sets are identified by an id in kernel space. Tweak with ip_set_id_t
+ * and IP_SET_INVALID_ID if you want to increase the max number of sets.
+ */
+typedef uint16_t ip_set_id_t;
+
+#define IP_SET_INVALID_ID 65535
+
+/* How deep we follow bindings (minus one) */
+#define IP_SET_MAX_BINDINGS 7
+
+/*
+ * Option flags for kernel operations (ipt_set_info)
+ */
+#define IPSET_SRC 0x01 /* Source match/add */
+#define IPSET_DST 0x02 /* Destination match/add */
+#define IPSET_MATCH_INV 0x04 /* Inverse matching */
+
+/*
+ * Set types (flavours)
+ */
+#define IPSET_TYPE_IP 0 /* IP address type of set */
+#define IPSET_TYPE_PORT 1 /* Port type of set */
+
+/* Reserved keywords */
+#define IPSET_TOKEN_DEFAULT ":default:"
+#define IPSET_TOKEN_ALL ":all:"
+
/* SO_IP_SET operation constants, and their request struct types.
+ *
+ * Operation ids:
+ * 0-99: commands with version checking
+ * 100-199: add/del/test/bind/unbind
+ * 200-299: list, save, restore
*/
-/* IP_SET_REQ_BASE defines the first components of ANY request structure.
- * It is used for all SO_IP_SET calls, set or get.
+/* Single shot operations:
+ * version, create, destroy, flush, rename and swap
+ *
+ * Sets are identified by name.
*/
-#define IP_SET_REQ_BASE \
- unsigned op; \
- int id \
-
-struct ip_set_req_base {
- IP_SET_REQ_BASE;
-};
-struct ip_set_req_std {
- IP_SET_REQ_BASE;
- ip_set_ip_t ip[IP_SET_SETIP_LEVELS];
- u_int8_t level;
-};
+#define IP_SET_REQ_STD \
+ unsigned op; \
+ unsigned version; \
+ char name[IP_SET_MAXNAMELEN]
#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */
struct ip_set_req_create {
- IP_SET_REQ_BASE;
- char name[IP_SET_MAXNAMELEN];
- char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN];
- u_int8_t levels;
+ IP_SET_REQ_STD;
+ char typename[IP_SET_MAXNAMELEN];
};
#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */
+struct ip_set_req_std {
+ IP_SET_REQ_STD;
+};
+
+#define IP_SET_OP_FLUSH 0x00000003 /* Remove all IPs in a set */
/* Uses ip_set_req_std */
-#define IP_SET_OP_CREATE_CHILD 0x00000003 /* Create a new child set */
-struct ip_set_req_sub {
- IP_SET_REQ_BASE;
- ip_set_ip_t ip[IP_SET_SETIP_LEVELS];
- u_int8_t level;
- u_int8_t childsets;
-};
+#define IP_SET_OP_RENAME 0x00000004 /* Rename a set */
+/* Uses ip_set_req_create */
-#define IP_SET_OP_FLUSH 0x00000004 /* Remove all IPs in a set */
-/* Uses ip_set_req_sub */
+#define IP_SET_OP_SWAP 0x00000005 /* Swap two sets */
+/* Uses ip_set_req_create */
-#define IP_SET_OP_RENAME 0x00000005 /* Rename a set */
-struct ip_set_req_rename {
- IP_SET_REQ_BASE;
- char newname[IP_SET_MAXNAMELEN];
+union ip_set_name_index {
+ char name[IP_SET_MAXNAMELEN];
+ ip_set_id_t index;
};
-#define IP_SET_OP_SWAP 0x00000006 /* Swap two sets */
-struct ip_set_req_swap {
- IP_SET_REQ_BASE;
- int to;
+#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */
+struct ip_set_req_get_set {
+ unsigned op;
+ unsigned version;
+ union ip_set_name_index set;
};
-#define IP_SET_OP_ADD_IP 0x00000007 /* Add an IP to a set */
-/* Uses ip_set_req_std, with type specific addage */
+#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */
+/* Uses ip_set_req_get_set */
-#define IP_SET_OP_DEL_IP 0x00000008 /* Remove an IP from a set */
-/* Uses ip_set_req_std, with type specific addage */
+#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
+struct ip_set_req_version {
+ unsigned op;
+ unsigned version;
+};
-/* Test if an IP is in the set
+/* Double shots operations:
+ * add, del, test, bind and unbind.
+ *
+ * First we query the kernel to get the index and type of the target set,
+ * then issue the command. Validity of IP is checked in kernel in order
+ * to minimalize sockopt operations.
*/
-#define IP_SET_OP_TEST_IP 0x00000009 /* Test an IP in a set */
-struct ip_set_req_test {
- IP_SET_REQ_BASE;
- ip_set_ip_t ip[IP_SET_SETIP_LEVELS];
- u_int8_t level;
- int reply; /* Test result */
+
+/* Get minimal set data for add/del/test/bind/unbind IP */
+#define IP_SET_OP_ADT_GET 0x00000010 /* Get set and type */
+struct ip_set_req_adt_get {
+ unsigned op;
+ unsigned version;
+ union ip_set_name_index set;
+ char typename[IP_SET_MAXNAMELEN];
};
-#define IP_SET_OP_VERSION 0x00000010
-struct ip_set_req_version {
- IP_SET_REQ_BASE;
+#define IP_SET_REQ_BYINDEX \
+ unsigned op; \
+ ip_set_id_t index;
+
+struct ip_set_req_adt {
+ IP_SET_REQ_BYINDEX;
+};
+
+#define IP_SET_OP_ADD_IP 0x00000101 /* Add an IP to a set */
+/* Uses ip_set_req_adt, with type specific addage */
+
+#define IP_SET_OP_DEL_IP 0x00000102 /* Remove an IP from a set */
+/* Uses ip_set_req_adt, with type specific addage */
+
+#define IP_SET_OP_TEST_IP 0x00000103 /* Test an IP in a set */
+/* Uses ip_set_req_adt, with type specific addage */
+
+#define IP_SET_OP_BIND_SET 0x00000104 /* Bind an IP to a set */
+/* Uses ip_set_req_bind, with type specific addage */
+struct ip_set_req_bind {
+ IP_SET_REQ_BYINDEX;
+ char binding[IP_SET_MAXNAMELEN];
+};
+
+#define IP_SET_OP_UNBIND_SET 0x00000105 /* Unbind an IP from a set */
+/* Uses ip_set_req_bind, with type speficic addage
+ * index = 0 means unbinding for all sets */
+
+#define IP_SET_OP_TEST_BIND_SET 0x00000106 /* Test binding an IP to a set */
+/* Uses ip_set_req_bind, with type specific addage */
+
+/* Multiple shots operations: list, save, restore.
+ *
+ * - check kernel version and query the max number of sets
+ * - get the basic information on all sets
+ * and size required for the next step
+ * - get actual set data: header, data, bindings
+ */
+
+/* Get max_sets and the index of a queried set
+ */
+#define IP_SET_OP_MAX_SETS 0x00000020
+struct ip_set_req_max_sets {
+ unsigned op;
unsigned version;
+ ip_set_id_t max_sets; /* max_sets */
+ ip_set_id_t sets; /* real number of sets */
+ union ip_set_name_index set; /* index of set if name used */
};
-/* List operations:
- * Size requests are sent by ip_set_req_list
- * except for LISTING.
- */
-#define IP_SET_OP_LIST_HEADER_SIZE 0x00000101
-#define IP_SET_OP_LIST_HEADER 0x00000102
-#define IP_SET_OP_LIST_MEMBERS_SIZE 0x00000103
-#define IP_SET_OP_LIST_MEMBERS 0x00000104
-#define IP_SET_OP_LIST_CHILDSETS_SIZE 0x00000105
-#define IP_SET_OP_LIST_CHILDSETS 0x00000106
+/* Get the id and name of the sets plus size for next step */
+#define IP_SET_OP_LIST_SIZE 0x00000201
+#define IP_SET_OP_SAVE_SIZE 0x00000202
+struct ip_set_req_setnames {
+ unsigned op;
+ ip_set_id_t index; /* set to list/save */
+ size_t size; /* size to get setdata/bindings */
+ /* followed by sets number of struct ip_set_name_list */
+};
+
+struct ip_set_name_list {
+ char name[IP_SET_MAXNAMELEN];
+ char typename[IP_SET_MAXNAMELEN];
+ ip_set_id_t index;
+ ip_set_id_t id;
+};
+
+/* The actual list operation */
+#define IP_SET_OP_LIST 0x00000203
struct ip_set_req_list {
- IP_SET_REQ_BASE;
- ip_set_ip_t ip[IP_SET_SETIP_LEVELS];
- u_int8_t level;
- size_t size;
+ IP_SET_REQ_BYINDEX;
+ /* sets number of struct ip_set_list in reply */
};
-#define IP_SET_OP_LISTING_SIZE 0x00000107
-#define IP_SET_OP_LISTING 0x00000108
+struct ip_set_list {
+ ip_set_id_t index;
+ ip_set_id_t binding;
+ u_int32_t ref;
+ size_t header_size; /* Set header data of header_size */
+ size_t members_size; /* Set members data of members_size */
+ size_t bindings_size; /* Set bindings data of bindings_size */
+};
-struct ip_set_req_listing_size {
- IP_SET_REQ_BASE;
- size_t size;
+struct ip_set_hash_list {
+ ip_set_ip_t ip;
+ ip_set_id_t binding;
};
-struct ip_set_req_listing {
- char name[IP_SET_MAXNAMELEN];
- char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN];
- u_int8_t levels;
- unsigned ref;
- int id;
+/* The save operation */
+#define IP_SET_OP_SAVE 0x00000204
+/* Uses ip_set_req_list, in the reply replaced by
+ * sets number of struct ip_set_save plus a marker
+ * ip_set_save followed by ip_set_hash_save structures.
+ */
+struct ip_set_save {
+ ip_set_id_t index;
+ ip_set_id_t binding;
+ size_t header_size; /* Set header data of header_size */
+ size_t members_size; /* Set members data of members_size */
};
-/* Between the iptables(8) set extension modules and the kernel we
- * identify a set by its id.
- *
- * The GETSET_BYNAME call passes the name of a set to the kernel, and
- * the a valid set id is returned if the set is still exist.
- * The GETSET_BYID call passes the id a set to the kernel, and
- * the set name is returned if the set is still exist.
+/* At restoring, ip == 0 means default binding for the given set: */
+struct ip_set_hash_save {
+ ip_set_ip_t ip;
+ ip_set_id_t id;
+ ip_set_id_t binding;
+};
+
+/* The restore operation */
+#define IP_SET_OP_RESTORE 0x00000205
+/* Uses ip_set_req_setnames followed by ip_set_restore structures
+ * plus a marker ip_set_restore, followed by ip_set_hash_save
+ * structures.
*/
-#define IP_SET_OP_GETSET_BYNAME 0x00000011
-struct ip_set_req_get {
- IP_SET_REQ_BASE;
- unsigned ref;
+struct ip_set_restore {
char name[IP_SET_MAXNAMELEN];
+ char typename[IP_SET_MAXNAMELEN];
+ ip_set_id_t index;
+ size_t header_size; /* Create data of header_size */
+ size_t members_size; /* Set members data of members_size */
};
-#define IP_SET_OP_GETSET_BYID 0x00000012
-/* Uses ip_set_req_get */
-
static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b)
{
return 4 * ((((b - a + 8) / 8) + 3) / 4);
@@ -233,28 +298,27 @@
printk(format "\n" , ## args); \
} while (0)
-#if defined(CONFIG_IP_NF_SET_DEBUG) || defined(CONFIG_IP_NF_SET_DEBUG_MODULE)
-#define CONFIG_IP_NF_SET_DEBUG
-#define CONFIG_IP_NF_SET_DEBUG_MODULE
-
+#if defined(IP_SET_DEBUG)
#define DP(format, args...) \
do { \
printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\
printk(format "\n" , ## args); \
} while (0)
+#define IP_SET_ASSERT(x) \
+ do { \
+ if (!(x)) \
+ printk("IP_SET_ASSERT: %s:%i(%s)\n", \
+ __FILE__, __LINE__, __FUNCTION__); \
+ } while (0)
#else
#define DP(format, args...)
+#define IP_SET_ASSERT(x)
#endif
-/* Generic set type: */
-struct ip_set_private {
- struct ip_set_private **childsets; /* child sets */
+struct ip_set;
- /* type speficic members */
-};
-
/*
- * The ip_set_type_t definition - one per set type, e.g. "ipmap".
+ * The ip_set_type definition - one per set type, e.g. "ipmap".
*
* Each individual set has a pointer, set->type, going to one
* of these structures. Function pointers inside the structure implement
@@ -267,28 +331,20 @@
struct ip_set_type {
struct list_head list; /* next in list of set types */
- /* match IP in set - internally required
- * return 0 if not in set, 1 if in set or
- * negative errno if input was invalid
- */
- int (*matchip) (struct ip_set_private *private,
- ip_set_ip_t ip,
- ip_set_ip_t *id);
-
- /* test for IP in set (kernel: iptables -m set --entry x)
+ /* test for IP in set (kernel: iptables -m set src|dst)
* return 0 if not in set, 1 if in set.
*/
- int (*testip_kernel) (struct ip_set_private *private,
+ int (*testip_kernel) (struct ip_set *set,
const struct sk_buff * skb,
u_int32_t flags,
- ip_set_ip_t *id);
+ ip_set_ip_t *ip);
- /* test for IP in set (userspace: ipset -T set --entry x)
+ /* test for IP in set (userspace: ipset -T set IP)
* return 0 if not in set, 1 if in set.
*/
- int (*testip) (struct ip_set_private *private,
+ int (*testip) (struct ip_set *set,
const void *data, size_t size,
- ip_set_ip_t *id);
+ ip_set_ip_t *ip);
/*
* Size of the data structure passed by when
@@ -296,63 +352,67 @@
*/
size_t reqsize;
- /* Add IP into set (userspace: ipset -A set --entry x)
+ /* Add IP into set (userspace: ipset -A set IP)
* Return -EEXIST if the address is already in the set,
* and -ERANGE if the address lies outside the set bounds.
* If the address was not already in the set, 0 is returned.
*/
- int (*addip) (struct ip_set_private *private,
+ int (*addip) (struct ip_set *set,
const void *data, size_t size,
- ip_set_ip_t *id);
+ ip_set_ip_t *ip);
- /* Add IP into set (kernel: iptables ... -j SET --entry x)
+ /* Add IP into set (kernel: iptables ... -j SET set src|dst)
* Return -EEXIST if the address is already in the set,
* and -ERANGE if the address lies outside the set bounds.
* If the address was not already in the set, 0 is returned.
*/
- int (*addip_kernel) (struct ip_set_private *private,
+ int (*addip_kernel) (struct ip_set *set,
const struct sk_buff * skb,
u_int32_t flags,
- ip_set_ip_t *id);
+ ip_set_ip_t *ip);
/* remove IP from set (userspace: ipset -D set --entry x)
* Return -EEXIST if the address is NOT in the set,
* and -ERANGE if the address lies outside the set bounds.
* If the address really was in the set, 0 is returned.
*/
- int (*delip) (struct ip_set_private *private,
+ int (*delip) (struct ip_set *set,
const void *data, size_t size,
- ip_set_ip_t *id);
+ ip_set_ip_t *ip);
/* remove IP from set (kernel: iptables ... -j SET --entry x)
* Return -EEXIST if the address is NOT in the set,
* and -ERANGE if the address lies outside the set bounds.
* If the address really was in the set, 0 is returned.
*/
- int (*delip_kernel) (struct ip_set_private *private,
+ int (*delip_kernel) (struct ip_set *set,
const struct sk_buff * skb,
u_int32_t flags,
- ip_set_ip_t *id);
+ ip_set_ip_t *ip);
/* new set creation - allocated type specific items
*/
- int (*create) (struct ip_set_private **private,
+ int (*create) (struct ip_set *set,
const void *data, size_t size);
+ /* retry the operation after successfully tweaking the set
+ */
+ int (*retry) (struct ip_set *set);
+
/* set destruction - free type specific items
* There is no return value.
* Can be called only when child sets are destroyed.
*/
- void (*destroy) (struct ip_set_private **private);
+ void (*destroy) (struct ip_set *set);
/* set flushing - reset all bits in the set, or something similar.
* There is no return value.
*/
- void (*flush) (struct ip_set_private *private);
+ void (*flush) (struct ip_set *set);
/* Listing: Get size needed for header
*/
- int (*list_header_size) (const struct ip_set_private *private);
+ int (*list_header_size) (const struct ip_set *set);
/* Listing: Get the header
*
@@ -361,12 +421,12 @@
* writelock on the set. Therefor is the length of "data" always
* correct.
*/
- void (*list_header) (const struct ip_set_private *private,
+ void (*list_header) (const struct ip_set *set,
void *data);
/* Listing: Get the size for the set members
*/
- int (*list_members_size) (const struct ip_set_private *private);
+ int (*list_members_size) (const struct ip_set *set);
/* Listing: Get the set members
*
@@ -375,18 +435,9 @@
* writelock on the set. Therefor is the length of "data" always
* correct.
*/
- void (*list_members) (const struct ip_set_private *private,
+ void (*list_members) (const struct ip_set *set,
void *data);
- /* Listing: set size in ids (first id is 0. Cannot change for a set).
- */
- ip_set_ip_t (*sizeid) (const struct ip_set_private *private);
-
- /* Listing: Get the bitmap for the valid childsets
- */
- void (*list_childsets) (const struct ip_set_private *private,
- void *data);
-
char typename[IP_SET_MAXNAMELEN];
char typecode;
int protocol_version;
@@ -400,40 +451,38 @@
/* A generic ipset */
struct ip_set {
- struct list_head list; /* next in list of all sets */
- rwlock_t lock; /* a lock for concurrency control */
- unsigned ref; /* reference counter */
- unsigned subref; /* reference counter at creating/destroying childsets */
- u_int8_t levels; /* max levels of subsets */
- struct ip_set_type *type[IP_SET_LEVELS]; /* the set types */
- struct ip_set_private *private; /* type specific data */
- char name[IP_SET_MAXNAMELEN]; /* the proper name of the set */
+ char name[IP_SET_MAXNAMELEN]; /* the name of the set */
+ rwlock_t lock; /* lock for concurrency control */
+ ip_set_id_t id; /* set id for swapping */
+ ip_set_id_t binding; /* default binding for the set */
+ atomic_t ref; /* in kernel and in hash references */
+ struct ip_set_type *type; /* the set types */
+ void *data; /* pooltype specific data */
};
-extern struct ip_set **ip_set_list;
+/* Structure to bind set elements to sets */
+struct ip_set_hash {
+ struct list_head list; /* list of clashing entries in hash */
+ ip_set_ip_t ip; /* ip from set */
+ ip_set_id_t id; /* set id */
+ ip_set_id_t binding; /* set we bind the element to */
+};
-/* register and unregister set pointer references */
-extern struct ip_set *ip_set_get_byname(const char name[IP_SET_MAXNAMELEN],
- int *id);
-extern struct ip_set *ip_set_get_byid(int id);
-extern void ip_set_put(struct ip_set *set);
+/* register and unregister set references */
+extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]);
+extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id);
+extern void ip_set_put(ip_set_id_t id);
/* API for iptables set match, and SET target */
-extern void ip_set_addip_kernel(struct ip_set *set,
+extern void ip_set_addip_kernel(ip_set_id_t id,
const struct sk_buff *skb,
- const u_int32_t *flags,
- u_int8_t set_level,
- u_int8_t ip_level);
-extern void ip_set_delip_kernel(struct ip_set *set,
+ const u_int32_t *flags);
+extern void ip_set_delip_kernel(ip_set_id_t id,
const struct sk_buff *skb,
- const u_int32_t *flags,
- u_int8_t set_level,
- u_int8_t ip_level);
-extern int ip_set_testip_kernel(struct ip_set *set,
+ const u_int32_t *flags);
+extern int ip_set_testip_kernel(ip_set_id_t id,
const struct sk_buff *skb,
- const u_int32_t *flags,
- u_int8_t set_level,
- u_int8_t ip_level);
+ const u_int32_t *flags);
#endif /* __KERNEL__ */
Modified: trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_iphash.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_iphash.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_iphash.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -7,24 +7,24 @@
#define MAX_RANGE 0x0000FFFF
struct ip_set_iphash {
- struct ip_set_private **childsets; /* child sets */
-
- /* Type speficic members: */
+ ip_set_ip_t *members; /* the iphash proper */
uint32_t initval; /* initval for jhash_1word */
- ip_set_ip_t hashsize; /* hash size */
+ uint32_t prime; /* prime for double hashing */
+ uint32_t hashsize; /* hash size */
+ uint16_t probes; /* max number of probes */
+ uint16_t resize; /* resize factor in percent */
ip_set_ip_t netmask; /* netmask */
- ip_set_ip_t *members; /* the iphash proper */
};
struct ip_set_req_iphash_create {
- uint32_t initval;
- ip_set_ip_t hashsize;
+ uint32_t hashsize;
+ uint16_t probes;
+ uint16_t resize;
ip_set_ip_t netmask;
};
struct ip_set_req_iphash {
ip_set_ip_t ip;
- u_int32_t flags;
};
#endif /* __IP_SET_IPHASH_H */
Modified: trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_ipmap.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_ipmap.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_ipmap.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -7,15 +7,12 @@
#define MAX_RANGE 0x0000FFFF
struct ip_set_ipmap {
- struct ip_set_private **childsets; /* child sets */
-
- /* Type speficic members: */
+ void *members; /* the ipmap proper */
ip_set_ip_t first_ip; /* host byte order, included in range */
ip_set_ip_t last_ip; /* host byte order, included in range */
ip_set_ip_t netmask; /* subnet netmask */
- ip_set_ip_t sizeid; /* size of set in ids */
- u_int16_t hosts; /* hosts per subnet */
- void *members; /* the ipmap proper */
+ ip_set_ip_t sizeid; /* size of set in IPs */
+ u_int16_t hosts; /* number of hosts in a subnet */
};
struct ip_set_req_ipmap_create {
Modified: trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_macipmap.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_macipmap.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_macipmap.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -13,12 +13,9 @@
#define IPSET_MACIP_ISSET 1
struct ip_set_macipmap {
- struct ip_set_private **childsets; /* child sets */
-
- /* Type speficic members: */
+ void *members; /* the macipmap proper */
ip_set_ip_t first_ip; /* host byte order, included in range */
ip_set_ip_t last_ip; /* host byte order, included in range */
- void *members; /* the macipmap proper */
u_int32_t flags;
};
Modified: trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_portmap.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_portmap.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_portmap.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -8,12 +8,9 @@
#define INVALID_PORT (MAX_RANGE + 1)
struct ip_set_portmap {
- struct ip_set_private **childsets; /* child sets */
-
- /* Type speficic members: */
+ void *members; /* the portmap proper */
ip_set_ip_t first_port; /* host byte order, included in range */
ip_set_ip_t last_port; /* host byte order, included in range */
- void *members; /* the portmap proper */
};
struct ip_set_req_portmap_create {
Added: trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_prime.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_prime.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ip_set_prime.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,34 @@
+#ifndef __IP_SET_PRIME_H
+#define __IP_SET_PRIME_H
+
+static inline unsigned make_prime_bound(unsigned nr)
+{
+ unsigned long long nr64 = nr;
+ unsigned long long x = 1;
+ nr = 1;
+ while (x <= nr64) { x <<= 2; nr <<= 1; }
+ return nr;
+}
+
+static inline int make_prime_check(unsigned nr)
+{
+ unsigned x = 3;
+ unsigned b = make_prime_bound(nr);
+ while (x <= b) {
+ if (0 == (nr % x)) return 0;
+ x += 2;
+ }
+ return 1;
+}
+
+static unsigned make_prime(unsigned nr)
+{
+ if (0 == (nr & 1)) nr--;
+ while (nr > 1) {
+ if (make_prime_check(nr)) return nr;
+ nr -= 2;
+ }
+ return 2;
+}
+
+#endif /* __IP_SET_PRIME_H */
Modified: trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ipt_set.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ipt_set.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/include/linux/netfilter_ipv4/ipt_set.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -4,14 +4,13 @@
#include <linux/netfilter_ipv4/ip_set.h>
struct ipt_set_info {
- int16_t id;
- u_int8_t set_level, ip_level;
- u_int32_t flags[IP_SET_LEVELS];
+ ip_set_id_t index;
+ u_int32_t flags[IP_SET_MAX_BINDINGS];
};
/* match info */
struct ipt_set_info_match {
- struct ipt_set_info match;
+ struct ipt_set_info match_set;
};
struct ipt_set_info_target {
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/Config.in.ladd
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/Config.in.ladd 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/Config.in.ladd 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,10 +1,11 @@
dep_tristate ' limit match support' CONFIG_IP_NF_MATCH_LIMIT $CONFIG_IP_NF_IPTABLES
- dep_tristate ' IP address/port sets support' CONFIG_IP_NF_SET $CONFIG_IP_NF_IPTABLES
+ dep_tristate ' IP set support' CONFIG_IP_NF_SET $CONFIG_IP_NF_IPTABLES
if [ "$CONFIG_IP_NF_SET" != "n" ]; then
int ' Maximum number of sets' CONFIG_IP_NF_SET_MAX 256
+ int ' Hash size for bindings of IP sets' CONFIG_IP_NF_SET_HASHSIZE 1024
dep_tristate ' ipmap set type support' CONFIG_IP_NF_SET_IPMAP $CONFIG_IP_NF_SET
dep_tristate ' portmap set type support' CONFIG_IP_NF_SET_PORTMAP $CONFIG_IP_NF_SET
dep_tristate ' macipmap set type support' CONFIG_IP_NF_SET_MACIPMAP $CONFIG_IP_NF_SET
dep_tristate ' iphash set type support' CONFIG_IP_NF_SET_IPHASH $CONFIG_IP_NF_SET
- fi
+ fi
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/Makefile.ladd
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/Makefile.ladd 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/Makefile.ladd 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,6 +1,8 @@
obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.o
-obj-$(CONFIG_IP_NF_SET) += ipt_set.o ipt_SET.o ip_set.o
+obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o
+obj-$(CONFIG_IP_NF_TARGET_SET) += ipt_SET.o
ifdef CONFIG_IP_NF_SET
+ obj-$(CONFIG_IP_NF_SET) += ip_set.o
export-objs += ip_set.o
endif
obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,33 +1,27 @@
-/* Copyright 2000-2004 Joakim Axelsson (gozem at linux.nu)
- * Patrick Schaaf (bof at bof.de)
- * Jozsef Kadlecsik (kadlec at blackhole.kfki.hu)
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at 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
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
/* Kernel module for IP set management */
#include <linux/config.h>
#include <linux/module.h>
+#include <linux/kmod.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
+#include <linux/random.h>
+#include <linux/jhash.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/softirq.h>
+#include <asm/semaphore.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
@@ -36,361 +30,271 @@
#include <linux/netfilter_ipv4/listhelp.h>
#include <linux/netfilter_ipv4/ip_set.h>
-static struct list_head set_type_list; /* all registred set types */
-struct ip_set **ip_set_list; /* all individual sets */
-static rwlock_t list_lock = RW_LOCK_UNLOCKED; /* protects both set_type_list and ip_set_list */
-static unsigned int max_sets = 0; /* max number of sets, */
+static struct list_head set_type_list; /* all registered sets */
+static struct ip_set **ip_set_list; /* all individual sets */
+static rwlock_t ip_set_lock = RW_LOCK_UNLOCKED; /* protects the lists and the hash */
+static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */
+static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX;
+static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE;
+static struct list_head *ip_set_hash; /* hash of bindings */
+static unsigned int ip_set_hash_random; /* random seed */
/* Arrgh */
#ifdef MODULE
-#define __MOD_INC(foo) __MOD_INC_USE_COUNT(foo)
-#define __MOD_DEC(foo) __MOD_DEC_USE_COUNT(foo)
+#define __MOD_INC(foo) __MOD_INC_USE_COUNT(foo)
+#define __MOD_DEC(foo) __MOD_DEC_USE_COUNT(foo)
+#define __MOD_INC_SELF MOD_INC_USE_COUNT
+#define __MOD_DEC_SELF MOD_DEC_USE_COUNT
#else
-#define __MOD_INC(foo) do { } while (0)
-#define __MOD_DEC(foo) do { } while (0)
+#define __MOD_INC(foo)
+#define __MOD_DEC(foo)
+#define __MOD_INC_SELF
+#define __MOD_DEC_SELF
#endif
-#define NOT_IN_CHILD_SET(fn,args...) \
- !*private \
- || !(*private)->childsets \
- || (set->type[i]->fn(*private,##args) < 0)
+/*
+ * Sets are identified either by the index in ip_set_list or by id.
+ * The id never changes and is used to find a key in the hash.
+ * The index may change by swapping and used at all other places
+ * (set/SET netfilter modules, binding value, etc.)
+ *
+ * Userspace requests are serialized by ip_set_mutex and sets can
+ * be deleted only from userspace. Therefore ip_set_list locking
+ * must obey the following rules:
+ *
+ * - kernel requests: read and write locking mandatory
+ * - user requests: read locking optional, write locking mandatory
+ */
-static struct ip_set_private **
-ip_set_find_private(struct ip_set *set,
- struct ip_set_private **private,
- ip_set_ip_t *ip,
- u_int8_t level)
+static inline void
+__ip_set_get(ip_set_id_t index)
{
- int i;
- ip_set_ip_t id;
+ atomic_inc(&ip_set_list[index]->ref);
+}
- for (i = 0; i < level; i++) {
- if (NOT_IN_CHILD_SET(matchip, ip[i], &id))
- return NULL;
- private = &(*private)->childsets[id];
- }
- DP("id: %i private: %p %p", id, private, *private);
- return private;
+static inline void
+__ip_set_put(ip_set_id_t index)
+{
+ atomic_dec(&ip_set_list[index]->ref);
}
-/* Destroy function for the private part of the (child)sets.
- * Must be called without holding any locks.
+/*
+ * Binding routines
*/
-static void
-ip_set_destroy_private(struct ip_set *set,
- struct ip_set_private **private,
- u_int8_t level)
+
+static inline int
+ip_hash_cmp(const struct ip_set_hash *set_hash,
+ ip_set_id_t id, ip_set_ip_t ip)
{
- int i;
+ return set_hash->id == id && set_hash->ip == ip;
+}
+
+static ip_set_id_t
+ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip)
+{
+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random)
+ % ip_set_bindings_hash_size;
+ struct ip_set_hash *set_hash;
+
+ MUST_BE_READ_LOCKED(&ip_set_lock);
+ IP_SET_ASSERT(ip_set_list[id]);
+ DP("set: %s, ip: %u", ip_set_list[id]->name, ip);
- DP("set %p private %p %p %p", set, private, *private, (*private)->childsets);
- if ((*private)->childsets) {
- for (i = 0; i < set->type[level]->sizeid(*private); i++)
- if ((*private)->childsets[i]) {
- DP("%i -> %p", i, (*private)->childsets[i]);
- ip_set_destroy_private(set,
- &(*private)->childsets[i],
- level + 1);
- }
- vfree((*private)->childsets);
- }
+ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
+ struct ip_set_hash *, id, ip);
+
+ DP("set: %s, ip: %u, binding: %s", ip_set_list[id]->name, ip,
+ set_hash != NULL ? ip_set_list[set_hash->binding]->name : "");
- set->type[level]->destroy(private);
- DP("%p %p", private, *private);
+ return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID);
}
-static void ip_set_flush_private(struct ip_set *set,
- struct ip_set_private *private,
- u_int8_t level,
- u_int8_t childsets)
+static inline void
+__set_hash_del(struct ip_set_hash *set_hash)
{
- int i;
+ MUST_BE_WRITE_LOCKED(&ip_set_lock);
+ IP_SET_ASSERT(ip_set_list[set_hash->binding]);
+
+ __ip_set_put(set_hash->binding);
+ list_del(&set_hash->list);
+ kfree(set_hash);
+}
+
+static int
+ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip)
+{
+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random)
+ % ip_set_bindings_hash_size;
+ struct ip_set_hash *set_hash;
- if (childsets && private->childsets)
- for (i = 0; i < set->type[level]->sizeid(private); i++)
- if (private->childsets[i])
- ip_set_flush_private(set,
- private->childsets[i],
- level + 1,
- childsets);
+ IP_SET_ASSERT(ip_set_list[id]);
+ DP("set: %s, ip: %u", ip_set_list[id]->name, ip);
+ write_lock_bh(&ip_set_lock);
+ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
+ struct ip_set_hash *, id, ip);
+ DP("set: %s, ip: %u, binding: %s", ip_set_list[id]->name, ip,
+ set_hash != NULL ? ip_set_list[set_hash->binding]->name : "");
- set->type[level]->flush(private);
-
+ if (set_hash != NULL)
+ __set_hash_del(set_hash);
+ write_unlock_bh(&ip_set_lock);
+ return 0;
}
-/* ip_set_flush() - flush data in a set
- */
-static int ip_set_flush(struct ip_set *set,
- ip_set_ip_t *ip,
- u_int8_t level,
- u_int8_t childsets)
+static int
+ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding)
{
- int res = 0;
- struct ip_set_private **private;
+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random)
+ % ip_set_bindings_hash_size;
+ struct ip_set_hash *set_hash;
+ int ret = 0;
- write_lock_bh(&set->lock);
- if (set->subref) {
- res = -EBUSY;
- goto unlock;
+ IP_SET_ASSERT(ip_set_list[id]);
+ IP_SET_ASSERT(ip_set_list[binding]);
+ DP("set: %s, ip: %u, binding: %s", ip_set_list[id]->name,
+ ip, ip_set_list[binding]->name);
+ write_lock_bh(&ip_set_lock);
+ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
+ struct ip_set_hash *, id, ip);
+ if (!set_hash) {
+ set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_KERNEL);
+ if (!set_hash) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+ INIT_LIST_HEAD(&set_hash->list);
+ set_hash->id = id;
+ set_hash->ip = ip;
+ list_add(&ip_set_hash[key], &set_hash->list);
+ } else {
+ IP_SET_ASSERT(ip_set_list[set_hash->binding]);
+ DP("overwrite binding: %s",
+ ip_set_list[set_hash->binding]->name);
+ __ip_set_put(set_hash->binding);
}
-
- private = ip_set_find_private(set, &set->private, ip, level);
-
- if (private)
- ip_set_flush_private(set, *private, level, childsets);
-
+ set_hash->binding = binding;
+ __ip_set_get(set_hash->binding);
unlock:
- write_unlock_bh(&set->lock);
- return res;
+ write_unlock_bh(&ip_set_lock);
+ return ret;
}
+#define FOREACH_HASH_DO(fn, args...) \
+({ \
+ ip_set_id_t __key; \
+ struct ip_set_hash *__set_hash; \
+ \
+ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \
+ list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \
+ fn(__set_hash , ## args); \
+ } \
+})
+
+#define FOREACH_HASH_RW_DO(fn, args...) \
+({ \
+ ip_set_id_t __key; \
+ struct ip_set_hash *__set_hash, *__n; \
+ \
+ MUST_BE_WRITE_LOCKED(&ip_set_lock); \
+ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \
+ list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\
+ fn(__set_hash , ## args); \
+ } \
+})
+
+/* Add, del and test set entries from kernel */
+
+#define follow_bindings(index, set, ip) \
+((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \
+ || (index = (set)->binding) != IP_SET_INVALID_ID)
+
int
-ip_set_testip_kernel(struct ip_set *set,
+ip_set_testip_kernel(ip_set_id_t index,
const struct sk_buff *skb,
- const u_int32_t *flags,
- u_int8_t set_level,
- u_int8_t ip_level)
+ const u_int32_t *flags)
{
- struct ip_set_private **private = &set->private;
- ip_set_ip_t id;
- int i, res = 0;
+ struct ip_set *set;
+ ip_set_ip_t ip;
+ int res, i = 0;
- read_lock_bh(&set->lock);
- if (set->levels < ip_level || set->subref)
- goto unlock;
-
- for (i = 0; i < set_level; i++) {
- if (NOT_IN_CHILD_SET(testip_kernel, skb,
- flags[i] | set->type[i]->typecode, &id))
- goto unlock;
- DP("id: %i private: %p", id, *private);
- private = &(*private)->childsets[id];
- }
- for (i = set_level; private && *private && i < ip_level; i++) {
- if (set->type[i]->testip_kernel(*private, skb,
- flags[i] | set->type[i]->typecode, &id) <= 0)
- goto unlock;
- private = (*private)->childsets
- ? &(*private)->childsets[id] : NULL;
- }
- res = 1;
- unlock:
- read_unlock_bh(&set->lock);
+ IP_SET_ASSERT(flags[i]);
+ read_lock_bh(&ip_set_lock);
+ do {
+ set = ip_set_list[index];
+ IP_SET_ASSERT(set);
+ DP("set %s, index %u", set->name, index);
+ read_lock_bh(&set->lock);
+ res = set->type->testip_kernel(set, skb, flags[i], &ip);
+ read_unlock_bh(&set->lock);
+ } while (res > 0
+ && flags[++i]
+ && follow_bindings(index, set, ip));
+ read_unlock_bh(&ip_set_lock);
+
return res;
}
void
-ip_set_addip_kernel(struct ip_set *set,
+ip_set_addip_kernel(ip_set_id_t index,
const struct sk_buff *skb,
- const u_int32_t *flags,
- u_int8_t set_level,
- u_int8_t ip_level)
+ const u_int32_t *flags)
{
- struct ip_set_private **private = &set->private;
- ip_set_ip_t id;
- int i, res;
+ struct ip_set *set;
+ ip_set_ip_t ip;
+ int res, i= 0;
- write_lock_bh(&set->lock);
- if (set->levels < ip_level || set->subref) {
+ IP_SET_ASSERT(flags[i]);
+ read_lock_bh(&ip_set_lock);
+ do {
+ set = ip_set_list[index];
+ IP_SET_ASSERT(set);
+ DP("set %s, index %u", set->name, index);
+ write_lock_bh(&set->lock);
+ res = set->type->addip_kernel(set, skb, flags[i], &ip);
write_unlock_bh(&set->lock);
- return;
- }
- for (i = 0; i < set_level; i++) {
- if (NOT_IN_CHILD_SET(testip_kernel, skb,
- flags[i] | set->type[i]->typecode, &id)) {
- write_unlock_bh(&set->lock);
- return;
- }
- private = &(*private)->childsets[id];
- }
- for (i = set_level; private && *private && i < ip_level; i++) {
- res = set->type[i]->addip_kernel(*private, skb,
- flags[i] | set->type[i]->typecode, &id);
- if (!(res == 0 || res == -EEXIST)) {
- write_unlock_bh(&set->lock);
- return;
- }
- private = (*private)->childsets
- ? &(*private)->childsets[id] : NULL;
- }
- write_unlock_bh(&set->lock);
+ } while ((res == -EAGAIN
+ && set->type->retry
+ && (res = set->type->retry(set)) == 0)
+ || ((res == 0 || res == -EEXIST)
+ && flags[++i]
+ && follow_bindings(index, set, ip)));
+ read_unlock_bh(&ip_set_lock);
}
void
-ip_set_delip_kernel(struct ip_set *set,
+ip_set_delip_kernel(ip_set_id_t index,
const struct sk_buff *skb,
- const u_int32_t *flags,
- u_int8_t set_level,
- u_int8_t ip_level)
+ const u_int32_t *flags)
{
- struct ip_set_private **private = &set->private;
- ip_set_ip_t id;
- int i, res;
+ struct ip_set *set;
+ ip_set_ip_t ip;
+ int res, i = 0;
- write_lock_bh(&set->lock);
- if (set->levels < ip_level || set->subref) {
+ IP_SET_ASSERT(flags[i]);
+ read_lock_bh(&ip_set_lock);
+ do {
+ set = ip_set_list[index];
+ IP_SET_ASSERT(set);
+ DP("set %s, index %u", set->name, index);
+ write_lock_bh(&set->lock);
+ res = set->type->delip_kernel(set, skb, flags[i], &ip);
write_unlock_bh(&set->lock);
- return;
- }
- for (i = 0; i < set_level; i++) {
- if (NOT_IN_CHILD_SET(testip_kernel, skb,
- flags[i] | set->type[i]->typecode, &id)) {
- write_unlock_bh(&set->lock);
- return;
- }
- private = &(*private)->childsets[id];
- }
- for (i = set_level; private && *private && i < ip_level; i++) {
- res = set->type[i]->delip_kernel(*private, skb,
- flags[i] | set->type[i]->typecode, &id);
- if (!(res == 0 || res == -EEXIST)) {
- write_unlock_bh(&set->lock);
- return;
- }
- private = (*private)->childsets
- ? &(*private)->childsets[id] : NULL;
- }
- write_unlock_bh(&set->lock);
+ } while ((res == 0 || res == -EEXIST)
+ && flags[++i]
+ && follow_bindings(index, set, ip));
+ read_unlock_bh(&ip_set_lock);
}
-static int
-ip_set_addip(struct ip_set *set,
- ip_set_ip_t *ip,
- u_int8_t level,
- const void *data,
- size_t size)
-{
- struct ip_set_private **private;
- ip_set_ip_t id;
- int res = 0;
+/* Register and deregister settype */
- DP("%s %i %d", set->name, level, size);
- write_lock_bh(&set->lock);
- if (set->subref) {
- res = -EBUSY;
- goto unlock;
- }
- private = ip_set_find_private(set, &set->private, ip, level);
- DP("%s %i %d", set->name, level, size);
- while (level <= set->levels && size) {
- DP("%s %i %d", set->name, level, size);
- if (!(private && *private)) {
- res = -ENOENT;
- goto unlock;
- }
- if (size < set->type[level]->reqsize) {
- res = -EINVAL;
- goto unlock;
- }
- res = set->type[level]->addip(*private, data,
- set->type[level]->reqsize, &id);
- if (!(res == 0 || res == -EEXIST))
- goto unlock;
- private = (*private)->childsets ? &(*private)->childsets[id] : NULL;
- data += set->type[level]->reqsize;
- size -= set->type[level++]->reqsize;
- }
- if (size)
- res = -EINVAL;
- unlock:
- write_unlock_bh(&set->lock);
- return res;
-}
-
-static int
-ip_set_delip(struct ip_set *set,
- ip_set_ip_t *ip,
- u_int8_t level,
- const void *data,
- size_t size)
-{
- struct ip_set_private **private;
- ip_set_ip_t id;
- int res = 0;
-
- write_lock_bh(&set->lock);
- if (set->subref) {
- res = -EBUSY;
- goto unlock;
- }
- private = ip_set_find_private(set, &set->private, ip, level);
- while (level <= set->levels && size) {
- if (!(private && *private)) {
- res = -ENOENT;
- goto unlock;
- }
- if (size < set->type[level]->reqsize) {
- res = -EINVAL;
- goto unlock;
- }
- res = set->type[level]->delip(*private, data,
- set->type[level]->reqsize, &id);
- if (!(res == 0 || res == -EEXIST))
- goto unlock;
- private = (*private)->childsets ? &(*private)->childsets[id] : NULL;
- data += set->type[level]->reqsize;
- size -= set->type[level++]->reqsize;
- }
- if (size)
- res = -EINVAL;
- unlock:
- write_unlock_bh(&set->lock);
- return res;
-}
-
-static int
-ip_set_testip(struct ip_set *set,
- ip_set_ip_t *ip,
- u_int8_t level,
- const void *data,
- size_t size)
-{
- struct ip_set_private **private;
- ip_set_ip_t id;
- int res = 0;
-
- write_lock_bh(&set->lock);
- if (set->subref) {
- res = -EBUSY;
- goto unlock;
- }
- private = ip_set_find_private(set, &set->private, ip, level);
- while (level <= set->levels && size) {
- if (!(private && *private)) {
- res = -ENOENT;
- goto unlock;
- }
- if (size < set->type[level]->reqsize) {
- res = -EINVAL;
- goto unlock;
- }
- res = set->type[level]->testip(*private, data,
- set->type[level]->reqsize, &id);
- DP("level: %i res: %i", level, res);
- if (res <= 0)
- goto unlock;
- private = (*private)->childsets ? &(*private)->childsets[id] : NULL;
- data += set->type[level]->reqsize;
- size -= set->type[level++]->reqsize;
- }
- if (size)
- res = -EINVAL;
- unlock:
- write_unlock_bh(&set->lock);
- return (res > 0);
-}
-
static inline int
set_type_equal(const struct ip_set_type *set_type, const char *str2)
{
- DP("'%s' vs. '%s'", set_type->typename, str2);
return !strncmp(set_type->typename, str2, IP_SET_MAXNAMELEN - 1);
}
-/*
- * Always use find_setfoo() under the &list_lock.
- */
-static inline struct ip_set_type *find_set_type(const char name[IP_SET_MAXNAMELEN])
+static inline struct ip_set_type *
+find_set_type(const char *name)
{
return LIST_FIND(&set_type_list,
set_type_equal,
@@ -398,8 +302,11 @@
name);
}
-int ip_set_register_set_type(struct ip_set_type *set_type)
+int
+ip_set_register_set_type(struct ip_set_type *set_type)
{
+ int ret = 0;
+
if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) {
ip_set_printk("'%s' uses wrong protocol version %u (want %u)",
set_type->typename,
@@ -408,141 +315,492 @@
return -EINVAL;
}
- write_lock_bh(&list_lock);
+ write_lock_bh(&ip_set_lock);
if (find_set_type(set_type->typename)) {
/* Duplicate! */
- write_unlock_bh(&list_lock);
ip_set_printk("'%s' already registered!",
set_type->typename);
- return -EINVAL;
+ ret = -EINVAL;
+ goto unlock;
}
- MOD_INC_USE_COUNT;
+ __MOD_INC_SELF;
list_append(&set_type_list, set_type);
- write_unlock_bh(&list_lock);
DP("'%s' registered.", set_type->typename);
- return 0;
+ unlock:
+ write_unlock_bh(&ip_set_lock);
+ return ret;
}
-void ip_set_unregister_set_type(struct ip_set_type *set_type)
+void
+ip_set_unregister_set_type(struct ip_set_type *set_type)
{
- write_lock_bh(&list_lock);
+ write_lock_bh(&ip_set_lock);
if (!find_set_type(set_type->typename)) {
ip_set_printk("'%s' not registered?",
set_type->typename);
- write_unlock_bh(&list_lock);
- return;
+ goto unlock;
}
LIST_DELETE(&set_type_list, set_type);
- write_unlock_bh(&list_lock);
- MOD_DEC_USE_COUNT;
+ __MOD_DEC_SELF;
+ DP("'%s' unregistered.", set_type->typename);
+ unlock:
+ write_unlock_bh(&ip_set_lock);
- DP("'%s' unregistered.", set_type->typename);
}
-/* Create the private part of a (child)set.
- * Must be called without holding any locks.
+/*
+ * Userspace routines
*/
-static int
-ip_set_create_private(struct ip_set_type *set_type,
- struct ip_set_private **private,
- const void *data,
- size_t size,
- u_int8_t childsets)
+
+/*
+ * Find set by name, reference it once. The reference makes sure the
+ * thing pointed to, does not go away under our feet. Drop the reference
+ * later, using ip_set_put().
+ */
+ip_set_id_t
+ip_set_get_byname(const char *name)
{
- int res = 0;
- int newbytes;
+ ip_set_id_t i, index = IP_SET_INVALID_ID;
+
+ down(&ip_set_app_mutex);
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && strcmp(ip_set_list[i]->name, name) == 0) {
+ __ip_set_get(i);
+ index = i;
+ break;
+ }
+ }
+ up(&ip_set_app_mutex);
+ return index;
+}
- DP("%s %p %p %i", set_type->typename, private, *private, childsets);
+/*
+ * Find set by index, reference it once. The reference makes sure the
+ * thing pointed to, does not go away under our feet. Drop the reference
+ * later, using ip_set_put().
+ */
+ip_set_id_t
+ip_set_get_byindex(ip_set_id_t index)
+{
+ down(&ip_set_app_mutex);
- if (*private)
- printk("%p: %p as private already occupied", private, *private);
+ if (index >= ip_set_max)
+ return IP_SET_INVALID_ID;
+
+ if (ip_set_list[index])
+ __ip_set_get(index);
+ else
+ index = IP_SET_INVALID_ID;
+
+ up(&ip_set_app_mutex);
+ return index;
+}
- /* Call the set_type initializer. */
- res = set_type->create(private, data, size);
- if (res != 0)
- return res;
+/*
+ * If the given set pointer points to a valid set, decrement
+ * reference count by 1. The caller shall not assume the index
+ * to be valid, after calling this function.
+ */
+void ip_set_put(ip_set_id_t index)
+{
+ down(&ip_set_app_mutex);
+ if (ip_set_list[index])
+ __ip_set_put(index);
+ up(&ip_set_app_mutex);
+}
- if (!childsets) {
- (*private)->childsets = NULL;
- return res;
+/* Find a set by name or index */
+static ip_set_id_t
+ip_set_find_byname(const char *name)
+{
+ ip_set_id_t i, index = IP_SET_INVALID_ID;
+
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && strcmp(ip_set_list[i]->name, name) == 0) {
+ index = i;
+ break;
+ }
}
-
- /* Create room for subsets */
- newbytes = set_type->sizeid(*private) * sizeof(struct ip_set_private *);
- DP("%s (%p) %i", set_type->typename, *private, newbytes);
- (*private)->childsets = vmalloc(newbytes); \
- if (!(*private)->childsets) {
- set_type->destroy(private);
- return -ENOMEM;
- }
- DP("%s %p %p %p", set_type->typename, private, *private, (*private)->childsets);
- memset((*private)->childsets, 0, newbytes);
+ return index;
+}
+
+static ip_set_id_t
+ip_set_find_byindex(ip_set_id_t index)
+{
+ if (index >= ip_set_max || ip_set_list[index] == NULL)
+ index = IP_SET_INVALID_ID;
+
+ return index;
+}
+
+/*
+ * Add, del, test, bind and unbind
+ */
+
+static inline int
+__ip_set_testip(struct ip_set *set,
+ const void *data,
+ size_t size,
+ ip_set_ip_t *ip)
+{
+ int res;
+
+ read_lock_bh(&set->lock);
+ res = set->type->testip(set, data, size, ip);
+ read_unlock_bh(&set->lock);
+
return res;
}
static int
-ip_set_create_childset(struct ip_set *set,
- ip_set_ip_t *ip,
- u_int8_t level,
- u_int8_t childsets,
- const void *data,
- size_t size)
+__ip_set_addip(ip_set_id_t index,
+ const void *data,
+ size_t size)
{
- struct ip_set_private **private = &set->private;
- ip_set_ip_t id;
+ struct ip_set *set = ip_set_list[index];
+ ip_set_ip_t ip;
int res;
+
+ IP_SET_ASSERT(set);
+ do {
+ write_lock_bh(&set->lock);
+ res = set->type->addip(set, data, size, &ip);
+ write_unlock_bh(&set->lock);
+ } while (res == -EAGAIN
+ && set->type->retry
+ && (res = set->type->retry(set)) == 0);
- DP("%s (%i %i)", set->name, level, childsets);
+ return res;
+}
+
+static int
+ip_set_addip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+
+ return __ip_set_addip(index,
+ data + sizeof(struct ip_set_req_adt),
+ size - sizeof(struct ip_set_req_adt));
+}
+
+static int
+ip_set_delip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
write_lock_bh(&set->lock);
- if (set->subref) {
- res = -EBUSY;
- goto unlock;
+ res = set->type->delip(set,
+ data + sizeof(struct ip_set_req_adt),
+ size - sizeof(struct ip_set_req_adt),
+ &ip);
+ write_unlock_bh(&set->lock);
+
+ return res;
+}
+
+static int
+ip_set_testip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
+ res = __ip_set_testip(set,
+ data + sizeof(struct ip_set_req_adt),
+ size - sizeof(struct ip_set_req_adt),
+ &ip);
+
+ return (res > 0 ? -EEXIST : res);
+}
+
+static int
+ip_set_bindip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ struct ip_set_req_bind *req_bind;
+ ip_set_id_t binding;
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
+ if (size < sizeof(struct ip_set_req_bind))
+ return -EINVAL;
+
+ req_bind = (struct ip_set_req_bind *) data;
+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) {
+ /* Default binding of a set */
+ char *binding_name;
+
+ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN)
+ return -EINVAL;
+
+ binding_name = (char *)(data + sizeof(struct ip_set_req_bind));
+ binding_name[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ binding = ip_set_find_byname(binding_name);
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+ write_lock_bh(&ip_set_lock);
+ /* Sets as binding values are referenced */
+ if (set->binding != IP_SET_INVALID_ID)
+ __ip_set_put(set->binding);
+ set->binding = binding;
+ __ip_set_get(set->binding);
+ write_unlock_bh(&ip_set_lock);
+
+ return 0;
}
- if (level > 1)
- private = ip_set_find_private(set, private, ip, level - 1);
- DP("%s (%i %i) %p %p", set->name, level, childsets, private, *private);
- if (!(private && *private && (*private)->childsets)) {
- res = -ENOENT;
- goto unlock;
+ binding = ip_set_find_byname(req_bind->binding);
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+ res = __ip_set_testip(set,
+ data + sizeof(struct ip_set_req_bind),
+ size - sizeof(struct ip_set_req_bind),
+ &ip);
+ DP("set %s, ip: %u, binding %s",
+ set->name, ip, ip_set_list[binding]->name);
+
+ if (res >= 0)
+ res = ip_set_hash_add(set->id, ip, binding);
+
+ return res;
+}
+
+#define FOREACH_SET_DO(fn, args...) \
+({ \
+ ip_set_id_t __i; \
+ struct ip_set *__set; \
+ \
+ for (__i = 0; __i < ip_set_max; __i++) { \
+ __set = ip_set_list[__i]; \
+ if (__set != NULL) \
+ fn(__set , ##args); \
+ } \
+})
+
+static inline void
+__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id)
+{
+ if (set_hash->id == id)
+ __set_hash_del(set_hash);
+}
+
+static inline void
+__unbind_default(struct ip_set *set)
+{
+ if (set->binding != IP_SET_INVALID_ID) {
+ /* Sets as binding values are referenced */
+ __ip_set_put(set->binding);
+ set->binding = IP_SET_INVALID_ID;
}
- DP("%s (%i %i) %p %p", set->name, level, childsets, private, *private);
- set->type[level - 1]->matchip(*private, ip[level - 1], &id);
- DP("%s (%i %i) %p %p %i", set->name, level, childsets, private, *private, id);
- if (id < 0) {
- res = -ENOENT;
- goto unlock;
+}
+
+static int
+ip_set_unbindip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set;
+ struct ip_set_req_bind *req_bind;
+ ip_set_ip_t ip;
+ int res;
+
+ DP("");
+ if (size < sizeof(struct ip_set_req_bind))
+ return -EINVAL;
+
+ req_bind = (struct ip_set_req_bind *) data;
+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ DP("%u %s", index, req_bind->binding);
+ if (index == IP_SET_INVALID_ID) {
+ /* unbind :all: */
+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) {
+ /* Default binding of sets */
+ write_lock_bh(&ip_set_lock);
+ FOREACH_SET_DO(__unbind_default);
+ write_unlock_bh(&ip_set_lock);
+ return 0;
+ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) {
+ /* Flush all bindings of all sets*/
+ write_lock_bh(&ip_set_lock);
+ FOREACH_HASH_RW_DO(__set_hash_del);
+ write_unlock_bh(&ip_set_lock);
+ return 0;
+ }
+ DP("unreachable reached!");
+ return -EINVAL;
}
- if ((*private)->childsets[id]) {
- res = -EEXIST;
- goto unlock;
+
+ set = ip_set_list[index];
+ IP_SET_ASSERT(set);
+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) {
+ /* Default binding of set */
+ ip_set_id_t binding = ip_set_find_byindex(set->binding);
+
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+ write_lock_bh(&ip_set_lock);
+ /* Sets in hash values are referenced */
+ __ip_set_put(set->binding);
+ set->binding = IP_SET_INVALID_ID;
+ write_unlock_bh(&ip_set_lock);
+
+ return 0;
+ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) {
+ /* Flush all bindings */
+
+ write_lock_bh(&ip_set_lock);
+ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id);
+ write_unlock_bh(&ip_set_lock);
+ return 0;
}
- set->subref++;
- write_unlock_bh(&set->lock);
- /* Without holding any locks, create private part. */
- res = ip_set_create_private(set->type[level],
- &(*private)->childsets[id],
- data, size, childsets);
+ res = __ip_set_testip(set,
+ data + sizeof(struct ip_set_req_bind),
+ size - sizeof(struct ip_set_req_bind),
+ &ip);
- write_lock_bh(&set->lock);
- set->subref--;
- unlock:
- DP("%s (%p %p) res=%i", set->name, private, *private, res);
- write_unlock_bh(&set->lock);
+ DP("set %s, ip: %u", set->name, ip);
+ if (res >= 0)
+ res = ip_set_hash_del(set->id, ip);
+
return res;
}
static int
-ip_set_create(const char name[IP_SET_MAXNAMELEN],
- char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN],
- u_int8_t level,
+ip_set_testbind(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ struct ip_set_req_bind *req_bind;
+ ip_set_id_t binding;
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
+ if (size < sizeof(struct ip_set_req_bind))
+ return -EINVAL;
+
+ req_bind = (struct ip_set_req_bind *) data;
+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) {
+ /* Default binding of set */
+ char *binding_name;
+
+ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN)
+ return -EINVAL;
+
+ binding_name = (char *)(data + sizeof(struct ip_set_req_bind));
+ binding_name[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ binding = ip_set_find_byname(binding_name);
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+ res = (set->binding == binding) ? -EEXIST : 0;
+
+ return res;
+ }
+ binding = ip_set_find_byname(req_bind->binding);
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+
+ res = __ip_set_testip(set,
+ data + sizeof(struct ip_set_req_bind),
+ size - sizeof(struct ip_set_req_bind),
+ &ip);
+ DP("set %s, ip: %u, binding %s",
+ set->name, ip, ip_set_list[binding]->name);
+
+ DP("set %s, ip: %u", set->name, ip);
+ if (res >= 0)
+ res = (ip_set_find_in_hash(set->id, ip) == binding)
+ ? -EEXIST : 0;
+
+ return res;
+}
+
+static struct ip_set_type *
+find_set_type_rlock(const char *typename)
+{
+ struct ip_set_type *type;
+
+ read_lock_bh(&ip_set_lock);
+ type = find_set_type(typename);
+ if (type == NULL)
+ read_unlock_bh(&ip_set_lock);
+
+ return type;
+}
+
+static int
+find_free_id(const char *name,
+ ip_set_id_t *index,
+ ip_set_id_t *id)
+{
+ ip_set_id_t i;
+
+ *id = IP_SET_INVALID_ID;
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] == NULL) {
+ if (*id == IP_SET_INVALID_ID)
+ *id = *index = i;
+ } else if (strcmp(name, ip_set_list[i]->name) == 0)
+ /* Name clash */
+ return -EEXIST;
+ }
+ if (*id == IP_SET_INVALID_ID)
+ /* No free slot remained */
+ return -ERANGE;
+ /* Check that index is usable as id (swapping) */
+ check:
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && ip_set_list[i]->id == *id) {
+ *id = i;
+ goto check;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Create a set
+ */
+static int
+ip_set_create(const char *name,
+ const char *typename,
+ ip_set_id_t restore,
const void *data,
size_t size)
{
- int i, id, res = 0;
struct ip_set *set;
+ ip_set_id_t index, id;
+ int res = 0;
- DP("%s (%i): %s", typename[0], level, name);
+ DP("setname: %s, typename: %s, id: %u", name, typename, restore);
/*
* First, and without any locks, allocate and initialize
* a normal base set structure.
@@ -552,45 +810,42 @@
return -ENOMEM;
set->lock = RW_LOCK_UNLOCKED;
strncpy(set->name, name, IP_SET_MAXNAMELEN);
- set->name[IP_SET_MAXNAMELEN - 1] = '\0';
- set->ref = 0;
- set->subref = 0;
- set->levels = level;
- set->private = NULL;
+ set->binding = IP_SET_INVALID_ID;
+ atomic_set(&set->ref, 0);
/*
- * Next, take the &list_lock, check that we know the type,
+ * Next, take the &ip_set_lock, check that we know the type,
* and take a reference on the type, to make sure it
* stays available while constructing our new set.
*
- * After referencing the type, we drop the &list_lock,
+ * After referencing the type, we drop the &ip_set_lock,
* and let the new set construction run without locks.
*/
- write_lock_bh(&list_lock);
- for (i = 0; i < level; i++) {
- set->type[i] = find_set_type(typename[i]);
- if (set->type[i] == NULL) {
- /* FIXME: try loading the module */
- write_unlock_bh(&list_lock);
- ip_set_printk("no set type '%s', set '%s' not created",
- typename[i], name);
- kfree(set);
- return -EINVAL;
- }
+ set->type = find_set_type_rlock(typename);
+ if (set->type == NULL) {
+ /* Try loading the module */
+ char modulename[IP_SET_MAXNAMELEN + strlen("ip_set_") + 1];
+ strcpy(modulename, "ip_set_");
+ strcat(modulename, typename);
+ DP("try to load %s", modulename);
+ request_module(modulename);
+ set->type = find_set_type_rlock(typename);
}
- for (i = 0; i < level; i++)
- __MOD_INC(set->type[i]->me);
- write_unlock_bh(&list_lock);
+ if (set->type == NULL) {
+ ip_set_printk("no set type '%s', set '%s' not created",
+ typename, name);
+ kfree(set);
+ return -ENOENT;
+ }
+ __MOD_INC(set->type->me);
+ read_unlock_bh(&ip_set_lock);
/*
* Without holding any locks, create private part.
*/
- res = ip_set_create_private(set->type[0],
- &set->private,
- data, size, level - 1);
+ res = set->type->create(set, data, size);
if (res != 0) {
- for (i = 0; i <= level; i++)
- __MOD_DEC(set->type[i]->me);
+ __MOD_DEC(set->type->me);
kfree(set);
return res;
}
@@ -598,164 +853,129 @@
/* BTW, res==0 here. */
/*
- * Here, we have a valid, constructed set. &list_lock again,
- * and check that it is not already in ip_set_list.
+ * Here, we have a valid, constructed set. &ip_set_lock again,
+ * find free id/index and check that it is not already in
+ * ip_set_list.
*/
- write_lock_bh(&list_lock);
- id = -1;
- for (i = 0; i < max_sets; i++) {
- if (ip_set_list[i] != NULL
- && strncmp(ip_set_list[i]->name, set->name,
- IP_SET_MAXNAMELEN - 1) == 0) {
- res = -EEXIST;
- goto cleanup;
- } else if (id < 0 && ip_set_list[i] == NULL)
- id = i;
+ write_lock_bh(&ip_set_lock);
+ if ((res = find_free_id(set->name, &index, &id)) != 0) {
+ DP("no free id!");
+ goto cleanup;
}
- if (id < 0) {
- /* No free slot remained */
+
+ /* Make sure restore gets the same index */
+ if (restore != IP_SET_INVALID_ID && index != restore) {
+ DP("Can't restore, sets are screwed up");
res = -ERANGE;
goto cleanup;
}
+
/*
- * Finally! Append our shiny new set into the list, and be done.
+ * Finally! Add our shiny new set to the list, and be done.
*/
- DP("create: '%s' created with id %i!", set->name, id);
- ip_set_list[id] = set;
- write_unlock_bh(&list_lock);
+ DP("create: '%s' created with index %u, id %u!", set->name, index, id);
+ set->id = id;
+ ip_set_list[index] = set;
+ write_unlock_bh(&ip_set_lock);
return res;
cleanup:
- write_unlock_bh(&list_lock);
- ip_set_destroy_private(set, &set->private, 0);
- for (i = 0; i < level; i++)
- __MOD_DEC(set->type[i]->me);
+ write_unlock_bh(&ip_set_lock);
+ set->type->destroy(set);
+ __MOD_DEC(set->type->me);
kfree(set);
return res;
}
-static int ip_set_destroy(struct ip_set *set,
- ip_set_ip_t *ip,
- u_int8_t level)
+/*
+ * Destroy a given existing set
+ */
+static void
+ip_set_destroy_set(ip_set_id_t index)
{
- struct ip_set_private **private;
- int i, res = 0;
+ struct ip_set *set = ip_set_list[index];
- write_lock_bh(&list_lock);
- /* there is no race, here. ->ref modification always happens
- * under &list_lock. Fine.
- */
- if (level == 0) {
- /* one ref from caller */
- if (set->ref > 1 || set->subref) {
- res = -EBUSY;
- goto unlock;
- }
+ IP_SET_ASSERT(set);
+ DP("set: %s", set->name);
+ write_lock_bh(&ip_set_lock);
+ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id);
+ if (set->binding != IP_SET_INVALID_ID)
+ __ip_set_put(set->binding);
+ ip_set_list[index] = NULL;
+ write_unlock_bh(&ip_set_lock);
- for (i = 0; i < max_sets; i++)
- if (ip_set_list[i] == set) {
- ip_set_list[i] = NULL;
- break;
- }
- write_unlock_bh(&list_lock);
-
- ip_set_destroy_private(set, &set->private, 0);
- for (i = 0; i < set->levels; i++)
- __MOD_DEC(set->type[i]->me);
- kfree(set);
- return res;
- }
-
- private = ip_set_find_private(set, &set->private,
- ip, level);
-
- if (private && *private) {
- if (set->subref) {
- res = -EBUSY;
- goto unlock;
- }
- set->subref++;
- write_unlock_bh(&list_lock);
-
- DP("%p %p", private, *private);
- ip_set_destroy_private(set, private, level);
- DP("%p %p", private, *private);
-
- write_lock_bh(&list_lock);
- set->subref--;
- } else
- res = -ENOENT;
-
- unlock:
- write_unlock_bh(&list_lock);
- return res;
+ /* Must call it without holding any lock */
+ set->type->destroy(set);
+ __MOD_DEC(set->type->me);
+ kfree(set);
}
/*
- * Find set by name, reference it once. The reference makes sure the
- * thing pointed to, does not go away under our feet. Drop the reference
- * later, using ip_set_put().
+ * Destroy a set - or all sets
+ * Sets must not be referenced/used.
*/
-struct ip_set *ip_set_get_byname(const char name[IP_SET_MAXNAMELEN],
- int *id)
+static int
+ip_set_destroy(ip_set_id_t index)
{
- struct ip_set *set = NULL;
- int i;
+ ip_set_id_t i;
- read_lock_bh(&list_lock);
- for (i = 0; i < max_sets; i++) {
- set = ip_set_list[i];
- if (set != NULL
- && strncmp(set->name, name, IP_SET_MAXNAMELEN - 1) == 0) {
- set->ref++;
- *id = i;
- break;
+ /* ref modification always protected by the mutex */
+ if (index != IP_SET_INVALID_ID) {
+ if (atomic_read(&ip_set_list[index]->ref))
+ return -EBUSY;
+ ip_set_destroy_set(index);
+ } else {
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && (atomic_read(&ip_set_list[i]->ref)))
+ return -EBUSY;
}
+
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL)
+ ip_set_destroy_set(i);
+ }
}
- read_unlock_bh(&list_lock);
- return set;
+ return 0;
}
-/*
- * Find set by id, reference it once. The reference makes sure the
- * thing pointed to, does not go away under our feet. Drop the reference
- * later, using ip_set_put().
- */
-struct ip_set *ip_set_get_byid(int id)
+static void
+ip_set_flush_set(struct ip_set *set)
{
- struct ip_set *set;
+ DP("set: %s %u", set->name, set->id);
- if (id < 0 || id >= max_sets)
- return NULL;
-
- write_lock_bh(&list_lock);
- set = ip_set_list[id];;
- if (set)
- set->ref++;
- write_unlock_bh(&list_lock);
- return set;
+ write_lock_bh(&set->lock);
+ set->type->flush(set);
+ write_unlock_bh(&set->lock);
}
-/*
- * If the given set pointer points to a valid set, decrement
- * reference count by 1. The caller shall not assume the pointer
- * to be valid, after calling this function.
+/*
+ * Flush data in a set - or in all sets
*/
-void ip_set_put(struct ip_set *set)
+static int
+ip_set_flush(ip_set_id_t index)
{
- write_lock_bh(&list_lock);
- if (set)
- set->ref--;
- write_unlock_bh(&list_lock);
+ if (index != IP_SET_INVALID_ID) {
+ IP_SET_ASSERT(ip_set_list[index]);
+ ip_set_flush_set(ip_set_list[index]);
+ } else
+ FOREACH_SET_DO(ip_set_flush_set);
+
+ return 0;
}
-static int ip_set_rename(struct ip_set *set, const char *name)
+/* Rename a set */
+static int
+ip_set_rename(ip_set_id_t index, const char *name)
{
- int i, res = 0;
+ struct ip_set *set = ip_set_list[index];
+ ip_set_id_t i;
+ int res = 0;
- write_lock_bh(&list_lock);
- for (i = 0; i < max_sets; i++) {
- if (ip_set_list[i] != NULL
+ DP("set: %s to %s", set->name, name);
+ write_lock_bh(&ip_set_lock);
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
&& strncmp(ip_set_list[i]->name,
name,
IP_SET_MAXNAMELEN - 1) == 0) {
@@ -764,224 +984,371 @@
}
}
strncpy(set->name, name, IP_SET_MAXNAMELEN);
- set->name[IP_SET_MAXNAMELEN - 1] = '\0';
unlock:
- write_unlock_bh(&list_lock);
+ write_unlock_bh(&ip_set_lock);
return res;
}
-static int ip_set_swap(struct ip_set *from, struct ip_set *to)
+/*
+ * Swap two sets so that name/index points to the other.
+ * References are also swapped.
+ */
+static int
+ip_set_swap(ip_set_id_t from_index, ip_set_id_t to_index)
{
+ struct ip_set *from = ip_set_list[from_index];
+ struct ip_set *to = ip_set_list[to_index];
char from_name[IP_SET_MAXNAMELEN];
- unsigned from_ref;
- int i, res = 0;
- int from_id = -1, to_id = -1;
+ u_int32_t from_ref;
- write_lock_bh(&list_lock);
- for (i = 0; i < max_sets && (from_id < 0 || to_id < 0); i++) {
- if (ip_set_list[i] == from)
- from_id = i;
- if (ip_set_list[i] == to)
- to_id = i;
- }
- /* We must have got both sets: we hold refcounts against them! */
- if (from_id < 0 || to_id < 0) {
- res = -EINVAL;
- goto unlock;
- }
-
+ DP("set: %s to %s", from->name, to->name);
+ /* Type can't be changed. Artifical restriction. */
+ if (from->type->typecode != to->type->typecode)
+ return -ENOEXEC;
+
+ /* No magic here: ref munging protected by the mutex */
+ write_lock_bh(&ip_set_lock);
strncpy(from_name, from->name, IP_SET_MAXNAMELEN);
- from_ref = from->ref;
+ from_ref = atomic_read(&from->ref);
+
+ strncpy(from->name, to->name, IP_SET_MAXNAMELEN);
+ atomic_set(&from->ref, atomic_read(&to->ref));
+ strncpy(to->name, from_name, IP_SET_MAXNAMELEN);
+ atomic_set(&to->ref, from_ref);
- ip_set_list[from_id] = to;
- ip_set_list[to_id] = from;
+ ip_set_list[from_index] = to;
+ ip_set_list[to_index] = from;
- strncpy(from->name, to->name, IP_SET_MAXNAMELEN);
- from->ref = to->ref;
- strncpy(to->name, from_name, IP_SET_MAXNAMELEN);
- to->ref = from_ref;
- unlock:
- write_unlock_bh(&list_lock);
- return res;
+ write_unlock_bh(&ip_set_lock);
+ return 0;
}
-size_t ip_set_listing_size(void)
+/*
+ * List set data
+ */
+
+static inline void
+__set_hash_bindings_size_list(struct ip_set_hash *set_hash,
+ ip_set_id_t id, size_t *size)
{
- size_t size = 0;
- int id;
+ if (set_hash->id == id)
+ *size += sizeof(struct ip_set_hash_list);
+}
- read_lock_bh(&list_lock);
- for (id = 0; id < max_sets; id++) {
- if (ip_set_list[id] != NULL)
- size += sizeof(struct ip_set_req_listing);
+static inline void
+__set_hash_bindings_size_save(struct ip_set_hash *set_hash,
+ ip_set_id_t id, size_t *size)
+{
+ if (set_hash->id == id)
+ *size += sizeof(struct ip_set_hash_save);
+}
+
+static inline void
+__set_hash_bindings(struct ip_set_hash *set_hash,
+ ip_set_id_t id, void *data)
+{
+ if (set_hash->id == id) {
+ struct ip_set_hash_list *hash_list =
+ (struct ip_set_hash_list *)data;
+
+ hash_list->ip = set_hash->ip;
+ hash_list->binding = set_hash->binding;
+ data += sizeof(struct ip_set_hash_list);
}
- read_unlock_bh(&list_lock);
-
- return size;
}
-int ip_set_listing(void *data, int *len)
+static int ip_set_list_set(ip_set_id_t index,
+ void *data,
+ int *used,
+ int len)
{
- int used = 0;
- int res = 0; /* All OK */
- int i, id;
- struct ip_set *set;
- struct ip_set_req_listing *header = data;
+ struct ip_set *set = ip_set_list[index];
+ struct ip_set_list *set_list;
- read_lock_bh(&list_lock);
- for (id = 0; id < max_sets; id++) {
- if (ip_set_list[id] == NULL)
- continue;
-
- /* Pointer to our header */
- header = (struct ip_set_req_listing *) (data + used);
+ /* Pointer to our header */
+ set_list = (struct ip_set_list *) (data + *used);
- DP("used before= %d %p %p %p", used, header, data,
- data + used);
+ DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used);
- /* Get and ensure header size */
- if (used + sizeof(struct ip_set_req_listing) > *len)
- goto not_enough_mem;
+ /* Get and ensure header size */
+ if (*used + sizeof(struct ip_set_list) > len)
+ goto not_enough_mem;
+ *used += sizeof(struct ip_set_list);
- set = ip_set_list[id];
+ read_lock_bh(&set->lock);
+ /* Get and ensure set specific header size */
+ set_list->header_size = set->type->list_header_size(set);
+ if (*used + set_list->header_size > len)
+ goto unlock_set;
- /* Fill with data */
- strncpy(header->name, set->name, IP_SET_MAXNAMELEN - 1);
- for (i = 0; i < set->levels; i++)
- strncpy(header->typename[i], set->type[i]->typename,
- IP_SET_MAXNAMELEN - 1);
- header->levels = set->levels;
- header->ref = set->ref;
- header->id = id;
+ /* Fill in the header */
+ set_list->index = index;
+ set_list->binding = set->binding;
+ set_list->ref = atomic_read(&set->ref);
- used += sizeof(struct ip_set_req_listing);
- DP("used after= %d", used);
- }
- *len = used; /* How much did we use? */
- goto unlock_and_return;
+ /* Fill in set spefific header data */
+ set->type->list_header(set, data + *used);
+ *used += set_list->header_size;
+ /* Get and ensure set specific members size */
+ set_list->members_size = set->type->list_members_size(set);
+ if (*used + set_list->members_size > len)
+ goto unlock_set;
+
+ /* Fill in set spefific members data */
+ set->type->list_members(set, data + *used);
+ *used += set_list->members_size;
+ read_unlock_bh(&set->lock);
+
+ /* Bindings */
+
+ /* Get and ensure set specific bindings size */
+ set_list->bindings_size = 0;
+ FOREACH_HASH_DO(__set_hash_bindings_size_list,
+ set->id, &set_list->bindings_size);
+ if (*used + set_list->bindings_size > len)
+ goto not_enough_mem;
+
+ /* Fill in set spefific bindings data */
+ FOREACH_HASH_DO(__set_hash_bindings, set->id, data + *used);
+ *used += set_list->bindings_size;
+
+ return 0;
+
+ unlock_set:
+ read_unlock_bh(&set->lock);
not_enough_mem:
DP("not enough mem, try again");
- res = -ENOMEM;
-
- unlock_and_return:
- read_unlock_bh(&list_lock);
- return res;
+ return -EAGAIN;
}
-int ip_set_list_size(struct ip_set * set,
- ip_set_ip_t *ip,
- unsigned level,
- size_t *size,
- unsigned op)
+/*
+ * Save sets
+ */
+static int ip_set_save_set(ip_set_id_t index,
+ void *data,
+ int *used,
+ int len)
{
- int res = 0; /* OK */
- struct ip_set_private **private;
+ struct ip_set *set;
+ struct ip_set_save *set_save;
- DP("%d %s %d", op, set->name, level);
+ /* Pointer to our header */
+ set_save = (struct ip_set_save *) (data + *used);
+
+ /* Get and ensure header size */
+ if (*used + sizeof(struct ip_set_save) > len)
+ goto not_enough_mem;
+ *used += sizeof(struct ip_set_save);
+
+ set = ip_set_list[index];
+ DP("set: %s, used: %u(%u) %p %p", set->name, *used, len,
+ data, data + *used);
+
read_lock_bh(&set->lock);
- if (set->subref) {
- res = -EBUSY;
- goto unlock;
- }
- private = ip_set_find_private(set, &set->private, ip, level);
- if (!(private && *private)) {
- res = -ENOENT;
- goto unlock;
- }
- switch (op) {
- case IP_SET_OP_LIST_HEADER_SIZE:
- *size = set->type[level]->list_header_size(*private);
- break;
- case IP_SET_OP_LIST_MEMBERS_SIZE:
- *size = set->type[level]->list_members_size(*private);
- break;
- case IP_SET_OP_LIST_CHILDSETS_SIZE:
- *size = (*private)->childsets == NULL ? 0
- : bitmap_bytes(0, set->type[level]->sizeid(*private) - 1);
- break;
- default:
- res = -EINVAL;
- }
- unlock:
+ /* Get and ensure set specific header size */
+ set_save->header_size = set->type->list_header_size(set);
+ if (*used + set_save->header_size > len)
+ goto unlock_set;
+
+ /* Fill in the header */
+ set_save->index = index;
+ set_save->binding = set->binding;
+
+ /* Fill in set spefific header data */
+ set->type->list_header(set, data + *used);
+ *used += set_save->header_size;
+
+ DP("set header filled: %s, used: %u %p %p", set->name, *used,
+ data, data + *used);
+ /* Get and ensure set specific members size */
+ set_save->members_size = set->type->list_members_size(set);
+ if (*used + set_save->members_size > len)
+ goto unlock_set;
+
+ /* Fill in set spefific members data */
+ set->type->list_members(set, data + *used);
+ *used += set_save->members_size;
read_unlock_bh(&set->lock);
- DP("%d %s %d: %u", op, set->name, level, *size);
+ DP("set members filled: %s, used: %u %p %p", set->name, *used,
+ data, data + *used);
+ return 0;
- return res;
+ unlock_set:
+ read_unlock_bh(&set->lock);
+ not_enough_mem:
+ DP("not enough mem, try again");
+ return -EAGAIN;
}
-static void list_childsets(const struct ip_set_private *private,
- void *data,
- ip_set_ip_t sizeid)
+static inline void
+__set_hash_save_bindings(struct ip_set_hash *set_hash,
+ ip_set_id_t id,
+ void *data,
+ int *used,
+ int len,
+ int *res)
{
- ip_set_ip_t id;
-
- memset(data, 0, bitmap_bytes(0, sizeid - 1));
+ if (*res == 0
+ && (id == IP_SET_INVALID_ID || set_hash->id == id)) {
+ struct ip_set_hash_save *hash_save =
+ (struct ip_set_hash_save *)(data + *used);
+ /* Ensure bindings size */
+ if (*used + sizeof(struct ip_set_hash_save) > len) {
+ *res = -ENOMEM;
+ return;
+ }
+ hash_save->id = set_hash->id;
+ hash_save->ip = set_hash->ip;
+ hash_save->binding = set_hash->binding;
+ *used += sizeof(struct ip_set_hash_save);
+ }
+}
- if (private->childsets == NULL)
- return;
-
- for (id = 0; id < sizeid; id++)
- if (private->childsets[id] != NULL)
- set_bit(id, data);
+static int ip_set_save_bindings(ip_set_id_t index,
+ void *data,
+ int *used,
+ int len)
+{
+ int res = 0;
+ struct ip_set_save *set_save;
+
+ DP("used %u, len %u", *used, len);
+ /* Get and ensure header size */
+ if (*used + sizeof(struct ip_set_save) > len)
+ return -ENOMEM;
+
+ /* Marker */
+ set_save = (struct ip_set_save *) (data + *used);
+ set_save->index = IP_SET_INVALID_ID;
+ *used += sizeof(struct ip_set_save);
+
+ DP("marker added used %u, len %u", *used, len);
+ /* Fill in bindings data */
+ if (index != IP_SET_INVALID_ID)
+ /* Sets are identified by id in hash */
+ index = ip_set_list[index]->id;
+ FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res);
+
+ return res;
}
-int ip_set_list_data(struct ip_set *set,
- ip_set_ip_t *ip,
- unsigned level,
- void *data,
- int *len,
- unsigned op)
+/*
+ * Restore sets
+ */
+static int ip_set_restore(void *data,
+ int len)
{
- int res = 0; /* All OK */
- size_t need;
- struct ip_set_private **private;
- void (*datafn)(const struct ip_set_private *, void *);
+ int res = 0;
+ int line = 0, used = 0, members_size;
+ struct ip_set *set;
+ struct ip_set_hash_save *hash_save;
+ struct ip_set_restore *set_restore;
+ ip_set_id_t index;
- read_lock_bh(&set->lock);
- if (set->subref) {
- res = -EBUSY;
- goto unlock;
- }
- private = ip_set_find_private(set, &set->private, ip, level);
- if (!(private && *private)) {
- res = -ENOENT;
- goto unlock;
- }
- switch (op) {
- case IP_SET_OP_LIST_HEADER:
- need = set->type[level]->list_header_size(*private);
- datafn = set->type[level]->list_header;
- break;
- case IP_SET_OP_LIST_MEMBERS:
- need = set->type[level]->list_members_size(*private);
- datafn = set->type[level]->list_members;
- break;
- case IP_SET_OP_LIST_CHILDSETS:
- if ((*private)->childsets == NULL) {
- res = -ENOENT;
- goto unlock;
+ /* Loop to restore sets */
+ while (1) {
+ line++;
+
+ DP("%u %u %u", used, sizeof(struct ip_set_restore), len);
+ /* Get and ensure header size */
+ if (used + sizeof(struct ip_set_restore) > len)
+ return line;
+ set_restore = (struct ip_set_restore *) (data + used);
+ used += sizeof(struct ip_set_restore);
+
+ /* Ensure data size */
+ if (used
+ + set_restore->header_size
+ + set_restore->members_size > len)
+ return line;
+
+ /* Check marker */
+ if (set_restore->index == IP_SET_INVALID_ID) {
+ line--;
+ goto bindings;
}
- need = bitmap_bytes(0, set->type[level]->sizeid(*private) - 1);
- datafn = NULL;
- break;
- default:
- res = -EINVAL;
- goto unlock;
+
+ /* Try to create the set */
+ DP("restore %s %s", set_restore->name, set_restore->typename);
+ res = ip_set_create(set_restore->name,
+ set_restore->typename,
+ set_restore->index,
+ data + used,
+ set_restore->header_size);
+
+ if (res != 0)
+ return line;
+ used += set_restore->header_size;
+
+ index = ip_set_find_byindex(set_restore->index);
+ DP("index %u, restore_index %u", index, set_restore->index);
+ if (index != set_restore->index)
+ return line;
+ /* Try to restore members data */
+ set = ip_set_list[index];
+ members_size = 0;
+ DP("members_size %u reqsize %u",
+ set_restore->members_size, set->type->reqsize);
+ while (members_size + set->type->reqsize <=
+ set_restore->members_size) {
+ line++;
+ DP("members: %u, line %u", members_size, line);
+ if (__ip_set_addip(index,
+ data + used + members_size,
+ set->type->reqsize)) {
+ return line;
+ }
+ members_size += set->type->reqsize;
+ }
+
+ DP("members_size %u %u",
+ set_restore->members_size, members_size);
+ if (members_size != set_restore->members_size)
+ return line++;
+ used += set_restore->members_size;
}
- if (need > *len) {
- res = -ENOMEM;
- goto unlock;
- }
- *len = need;
- if (datafn)
- datafn(*private, data);
- else
- list_childsets(*private, data, set->type[level]->sizeid(*private));
+
+ bindings:
+ /* Loop to restore bindings */
+ while (used < len) {
+ line++;
- unlock:
- read_unlock_bh(&set->lock);
- return res;
+ DP("restore binding, line %u", line);
+ /* Get and ensure size */
+ if (used + sizeof(struct ip_set_hash_save) > len)
+ return line;
+ hash_save = (struct ip_set_hash_save *) (data + used);
+ used += sizeof(struct ip_set_hash_save);
+
+ /* hash_save->id is used to store the index */
+ index = ip_set_find_byindex(hash_save->id);
+ DP("restore binding index %u, id %u, %u -> %u",
+ index, hash_save->id, hash_save->ip, hash_save->binding);
+ if (index != hash_save->id)
+ return line;
+
+ set = ip_set_list[hash_save->id];
+ /* Null valued IP means default binding */
+ if (hash_save->ip)
+ res = ip_set_hash_add(set->id,
+ hash_save->ip,
+ hash_save->binding);
+ else {
+ IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID);
+ write_lock_bh(&ip_set_lock);
+ set->binding = hash_save->binding;
+ __ip_set_get(set->binding);
+ write_unlock_bh(&ip_set_lock);
+ DP("default binding: %u", set->binding);
+ }
+ if (res != 0)
+ return line;
+ }
+ if (used != len)
+ return line;
+
+ return 0;
}
static int
@@ -989,18 +1356,27 @@
{
void *data;
int res = 0; /* Assume OK */
- struct ip_set_req_base *req_base;
- struct ip_set_req_std *req_std;
- struct ip_set *set = NULL;
+ unsigned *op;
+ struct ip_set_req_adt *req_adt;
+ ip_set_id_t index = IP_SET_INVALID_ID;
+ int (*adtfn)(ip_set_id_t index,
+ const void *data, size_t size);
+ struct fn_table {
+ int (*fn)(ip_set_id_t index,
+ const void *data, size_t size);
+ } adtfn_table[] =
+ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip},
+ { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind },
+ };
DP("optval=%d, user=%p, len=%d", optval, user, len);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (optval != SO_IP_SET)
return -EBADF;
- if (len < sizeof(struct ip_set_req_base)) {
- ip_set_printk("short userdata (want >=%d, got %d)",
- sizeof(struct ip_set_req_base), len);
+ if (len <= sizeof(unsigned)) {
+ ip_set_printk("short userdata (want >%d, got %d)",
+ sizeof(unsigned), len);
return -EINVAL;
}
data = vmalloc(len);
@@ -1012,183 +1388,181 @@
res = -EFAULT;
goto done;
}
+ if (down_interruptible(&ip_set_app_mutex)) {
+ res = -EINTR;
+ goto done;
+ }
- req_base = (struct ip_set_req_base *) data;
-
- DP("op=%x id='%x'", req_base->op, req_base->id);
+ op = (unsigned *)data;
+ DP("op=%x", *op);
- /* Handle set creation first - no incoming set specified */
+ if (*op < IP_SET_OP_VERSION) {
+ /* Check the version at the beginning of operations */
+ struct ip_set_req_version *req_version =
+ (struct ip_set_req_version *) data;
+ if (req_version->version != IP_SET_PROTOCOL_VERSION) {
+ res = -EPROTO;
+ goto done;
+ }
+ }
- if (req_base->op == IP_SET_OP_CREATE) {
+ switch (*op) {
+ case IP_SET_OP_CREATE:{
struct ip_set_req_create *req_create
= (struct ip_set_req_create *) data;
- int i;
- if (len < sizeof(struct ip_set_req_create)) {
+ if (len <= sizeof(struct ip_set_req_create)) {
ip_set_printk("short CREATE data (want >%d, got %d)",
sizeof(struct ip_set_req_create), len);
res = -EINVAL;
goto done;
}
- if (req_create->levels > IP_SET_LEVELS) {
- ip_set_printk("set level %d too deep (max %d)",
- req_create->levels, IP_SET_LEVELS);
- res = -EINVAL;
- goto done;
- }
req_create->name[IP_SET_MAXNAMELEN - 1] = '\0';
- for (i = 0; i < req_create->levels; i++)
- req_create->typename[i][IP_SET_MAXNAMELEN - 1] = '\0';
+ req_create->typename[IP_SET_MAXNAMELEN - 1] = '\0';
res = ip_set_create(req_create->name,
req_create->typename,
- req_create->levels,
+ IP_SET_INVALID_ID,
data + sizeof(struct ip_set_req_create),
len - sizeof(struct ip_set_req_create));
goto done;
}
+ case IP_SET_OP_DESTROY:{
+ struct ip_set_req_std *req_destroy
+ = (struct ip_set_req_std *) data;
+
+ if (len != sizeof(struct ip_set_req_std)) {
+ ip_set_printk("invalid DESTROY data (want %d, got %d)",
+ sizeof(struct ip_set_req_std), len);
+ res = -EINVAL;
+ goto done;
+ }
+ if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) {
+ /* Destroy all sets */
+ index = IP_SET_INVALID_ID;
+ } else {
+ req_destroy->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byname(req_destroy->name);
- /* All remaining requests want a set by id.
- * We take a proper reference here, and drop it after processing.
- * From hereon, code goes to '*put_set', not to 'done'.
- */
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ }
+
+ res = ip_set_destroy(index);
+ goto done;
+ }
+ case IP_SET_OP_FLUSH:{
+ struct ip_set_req_std *req_flush =
+ (struct ip_set_req_std *) data;
- set = ip_set_get_byid(req_base->id);
- if (set == NULL) {
- res = -ESRCH;
+ if (len != sizeof(struct ip_set_req_std)) {
+ ip_set_printk("invalid FLUSH data (want %d, got %d)",
+ sizeof(struct ip_set_req_std), len);
+ res = -EINVAL;
+ goto done;
+ }
+ if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) {
+ /* Flush all sets */
+ index = IP_SET_INVALID_ID;
+ } else {
+ req_flush->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byname(req_flush->name);
+
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ }
+ res = ip_set_flush(index);
goto done;
- }
-
- DP("set %s (%d) (%u)", set->name, req_base->id, set->ref);
- /* Simple requests: no subsets */
- switch (req_base->op) {
+ }
case IP_SET_OP_RENAME:{
- struct ip_set_req_rename *req_rename
- = (struct ip_set_req_rename *) data;
+ struct ip_set_req_create *req_rename
+ = (struct ip_set_req_create *) data;
- if (len != sizeof(struct ip_set_req_rename)) {
- ip_set_printk("short RENAME data (want >%d, got %d)",
- sizeof(struct ip_set_req_rename), len);
- res = -EINVAL;
- goto put_set;
- }
+ if (len != sizeof(struct ip_set_req_create)) {
+ ip_set_printk("invalid RENAME data (want %d, got %d)",
+ sizeof(struct ip_set_req_create), len);
+ res = -EINVAL;
+ goto done;
+ }
- res = ip_set_rename(set, req_rename->newname);
- goto put_set;
+ req_rename->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ req_rename->typename[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ index = ip_set_find_byname(req_rename->name);
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
}
-
+ res = ip_set_rename(index, req_rename->typename);
+ goto done;
+ }
case IP_SET_OP_SWAP:{
- struct ip_set_req_swap *req_swap
- = (struct ip_set_req_swap *) data;
- struct ip_set *to;
+ struct ip_set_req_create *req_swap
+ = (struct ip_set_req_create *) data;
+ ip_set_id_t to_index;
- if (len != sizeof(struct ip_set_req_swap)) {
+ if (len != sizeof(struct ip_set_req_create)) {
+ ip_set_printk("invalid SWAP data (want %d, got %d)",
+ sizeof(struct ip_set_req_create), len);
+ res = -EINVAL;
+ goto done;
+ }
- ip_set_printk("short SWAP data (want >%d, got %d)",
- sizeof(struct ip_set_req_swap), len);
- res = -EINVAL;
- goto put_set;
- }
+ req_swap->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ req_swap->typename[IP_SET_MAXNAMELEN - 1] = '\0';
- to = ip_set_get_byid(req_swap->to);
- if (to == NULL) {
- res = -ESRCH;
- goto put_set;
- }
- res = ip_set_swap(set, to);
- ip_set_put(to);
- goto put_set;
+ index = ip_set_find_byname(req_swap->name);
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
}
+ to_index = ip_set_find_byname(req_swap->typename);
+ if (to_index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ res = ip_set_swap(index, to_index);
+ goto done;
+ }
default:
- ; /* Requests with possible subsets: fall trough. */
+ break; /* Set identified by id */
}
- req_std = (struct ip_set_req_std *) data;
- if (len < sizeof(struct ip_set_req_std)) {
- ip_set_printk("short data in std request (want >%d, got %d)",
- sizeof(struct ip_set_req_std), len);
+ /* There we may have add/del/test/bind/unbind/test_bind operations */
+ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) {
+ res = -EBADMSG;
+ goto done;
+ }
+ adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn;
+
+ if (len < sizeof(struct ip_set_req_adt)) {
+ ip_set_printk("short data in adt request (want >=%d, got %d)",
+ sizeof(struct ip_set_req_adt), len);
res = -EINVAL;
- goto put_set;
- } else if (req_std->level >= set->levels) {
- res = -EINVAL;
- goto put_set;
+ goto done;
}
+ req_adt = (struct ip_set_req_adt *) data;
- switch (req_base->op) {
- case IP_SET_OP_ADD_IP:{
- res = ip_set_addip(set,
- req_std->ip, req_std->level,
- data + sizeof(struct ip_set_req_std),
- len - sizeof(struct ip_set_req_std));
- goto put_set;
+ /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */
+ if (!(*op == IP_SET_OP_UNBIND_SET
+ && req_adt->index == IP_SET_INVALID_ID)) {
+ index = ip_set_find_byindex(req_adt->index);
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
}
- case IP_SET_OP_DEL_IP:{
- res = ip_set_delip(set,
- req_std->ip, req_std->level,
- data + sizeof(struct ip_set_req_std),
- len - sizeof(struct ip_set_req_std));
- goto put_set;
- }
- case IP_SET_OP_DESTROY:{
- res = ip_set_destroy(set, req_std->ip, req_std->level);
- if (req_std->level == 0 && res == 0)
- goto done; /* destroyed: no ip_set_put */
- goto put_set;
- }
- case IP_SET_OP_FLUSH:{
- struct ip_set_req_sub *req_sub =
- (struct ip_set_req_sub *) data;
+ }
+ res = adtfn(index, data, len);
- if (len < sizeof(struct ip_set_req_sub)) {
- ip_set_printk("short data in flush request (want >%d, got %d)",
- sizeof(struct ip_set_req_sub), len);
- res = -EINVAL;
- goto put_set;
- }
- res = ip_set_flush(set, req_sub->ip, req_sub->level, req_sub->childsets);
- goto put_set;
- }
- case IP_SET_OP_CREATE_CHILD:{
- struct ip_set_req_sub *req_sub
- = (struct ip_set_req_sub *) data;
-
- if (len < sizeof(struct ip_set_req_sub)) {
- ip_set_printk("short CREATE_CHILD data (want >%d, got %d)",
- sizeof(struct ip_set_req_sub), len);
- res = -EINVAL;
- goto put_set;
- }
- if (req_sub->level < 1) {
- /* No entry supplied? */
- res = -EINVAL;
- goto put_set;
- }
- if (((req_sub->level >= set->levels - 1) && req_sub->childsets)) {
- /* No room for subsets to be created. */
- res = -ERANGE;
- goto put_set;
- }
- res = ip_set_create_childset(set,
- req_sub->ip,
- req_sub->level,
- req_sub->childsets,
- data + sizeof(struct ip_set_req_sub),
- len - sizeof(struct ip_set_req_sub));
- goto put_set;
- }
- default:{
- DP("unknown op %d", req_base->op);
- ip_set_printk("obsolete - upgrade your ipset(8) utility.");
- res = -EINVAL;
- }
- } /* end of switch(op) */
-
- put_set:
- if (set)
- ip_set_put(set);
done:
+ up(&ip_set_app_mutex);
vfree(data);
if (res > 0)
res = 0;
+ DP("final result %d", res);
return res;
}
@@ -1196,9 +1570,8 @@
ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len)
{
int res = 0;
- struct ip_set_req_base *req_base;
- struct ip_set_req_std *req_std;
- struct ip_set *set = NULL;
+ unsigned *op;
+ ip_set_id_t index = IP_SET_INVALID_ID;
void *data;
int copylen = *len;
@@ -1207,9 +1580,9 @@
return -EPERM;
if (optval != SO_IP_SET)
return -EBADF;
- if (*len < sizeof(struct ip_set_req_base)) {
+ if (*len < sizeof(unsigned)) {
ip_set_printk("short userdata (want >=%d, got %d)",
- sizeof(struct ip_set_req_base), *len);
+ sizeof(unsigned), *len);
return -EINVAL;
}
data = vmalloc(*len);
@@ -1221,211 +1594,317 @@
res = -EFAULT;
goto done;
}
+ if (down_interruptible(&ip_set_app_mutex)) {
+ res = -EINTR;
+ goto done;
+ }
- req_base = (struct ip_set_req_base *) data;
+ op = (unsigned *) data;
+ DP("op=%x", *op);
- DP("op=%x id='%x'", req_base->op, req_base->id);
+ if (*op < IP_SET_OP_VERSION) {
+ /* Check the version at the beginning of operations */
+ struct ip_set_req_version *req_version =
+ (struct ip_set_req_version *) data;
+ if (req_version->version != IP_SET_PROTOCOL_VERSION) {
+ res = -EPROTO;
+ goto done;
+ }
+ }
- /* Requests without a named set. */
- switch (req_base->op) {
- case IP_SET_OP_VERSION:{
- struct ip_set_req_version *req_version =
- (struct ip_set_req_version *) data;
+ switch (*op) {
+ case IP_SET_OP_VERSION: {
+ struct ip_set_req_version *req_version =
+ (struct ip_set_req_version *) data;
- if (*len != sizeof(struct ip_set_req_version)) {
- ip_set_printk("short VERSION (want >=%d, got %d)",
- sizeof(struct ip_set_req_version),
- *len);
- res = -EINVAL;
- goto done;
- }
-
- req_version->version = IP_SET_PROTOCOL_VERSION;
- res = copy_to_user(user, req_version,
- sizeof(struct ip_set_req_version));
+ if (*len != sizeof(struct ip_set_req_version)) {
+ ip_set_printk("invalid VERSION (want %d, got %d)",
+ sizeof(struct ip_set_req_version),
+ *len);
+ res = -EINVAL;
goto done;
}
- case IP_SET_OP_LISTING_SIZE:{
- struct ip_set_req_listing_size *req_list =
- (struct ip_set_req_listing_size *) data;
- DP("IP_SET_OP_LISTING_SIZE");
+ req_version->version = IP_SET_PROTOCOL_VERSION;
+ res = copy_to_user(user, req_version,
+ sizeof(struct ip_set_req_version));
+ goto done;
+ }
+ case IP_SET_OP_GET_BYNAME: {
+ struct ip_set_req_get_set *req_get
+ = (struct ip_set_req_get_set *) data;
- if (*len != sizeof(struct ip_set_req_listing_size)) {
- ip_set_printk("short LISTING_SIZE (want >=%d, got %d)",
- sizeof(struct ip_set_req_listing_size),
- *len);
- res = -EINVAL;
- goto done;
- }
+ if (*len != sizeof(struct ip_set_req_get_set)) {
+ ip_set_printk("invalid GET_BYNAME (want %d, got %d)",
+ sizeof(struct ip_set_req_get_set), *len);
+ res = -EINVAL;
+ goto done;
+ }
+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byname(req_get->set.name);
+ req_get->set.index = index;
+ goto copy;
+ }
+ case IP_SET_OP_GET_BYINDEX: {
+ struct ip_set_req_get_set *req_get
+ = (struct ip_set_req_get_set *) data;
- req_list->size = ip_set_listing_size();
- DP("req_list->size = %d", req_list->size);
- res = copy_to_user(user, req_list,
- sizeof(struct ip_set_req_listing_size));
+ if (*len != sizeof(struct ip_set_req_get_set)) {
+ ip_set_printk("invalid GET_BYINDEX (want %d, got %d)",
+ sizeof(struct ip_set_req_get_set), *len);
+ res = -EINVAL;
goto done;
}
- case IP_SET_OP_LISTING:{
- DP("LISTING before len=%d", *len);
- res = ip_set_listing(data, len);
- DP("LISTING done len=%d", *len);
- if (res < 0)
- goto done; /* Error */
+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byindex(req_get->set.index);
+ strncpy(req_get->set.name,
+ index == IP_SET_INVALID_ID ? ""
+ : ip_set_list[index]->name, IP_SET_MAXNAMELEN);
+ goto copy;
+ }
+ case IP_SET_OP_ADT_GET: {
+ struct ip_set_req_adt_get *req_get
+ = (struct ip_set_req_adt_get *) data;
- res = copy_to_user(user, data, *len); /* Only copy the mem used */
+ if (*len != sizeof(struct ip_set_req_adt_get)) {
+ ip_set_printk("invalid ADT_GET (want %d, got %d)",
+ sizeof(struct ip_set_req_adt_get), *len);
+ res = -EINVAL;
goto done;
}
- default:
- ; /* Requests with named set: fall trought */
+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byname(req_get->set.name);
+ if (index != IP_SET_INVALID_ID) {
+ req_get->set.index = index;
+ strncpy(req_get->typename,
+ ip_set_list[index]->type->typename,
+ IP_SET_MAXNAMELEN - 1);
+ } else {
+ res = -ENOENT;
+ goto done;
+ }
+ goto copy;
}
+ case IP_SET_OP_MAX_SETS: {
+ struct ip_set_req_max_sets *req_max_sets
+ = (struct ip_set_req_max_sets *) data;
+ ip_set_id_t i;
- /* Special cases: GETSET_BYNAME/BYID */
- switch (req_base->op) {
- case IP_SET_OP_GETSET_BYNAME: {
- struct ip_set_req_get *req_get
- = (struct ip_set_req_get *) data;
+ if (*len != sizeof(struct ip_set_req_max_sets)) {
+ ip_set_printk("invalid MAX_SETS (want %d, got %d)",
+ sizeof(struct ip_set_req_max_sets), *len);
+ res = -EINVAL;
+ goto done;
+ }
- if (*len != sizeof(struct ip_set_req_get)) {
- ip_set_printk("short _BYNAME (want >=%d, got %d)",
- sizeof(struct ip_set_req_get), *len);
- res = -EINVAL;
+ if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) {
+ req_max_sets->set.index = IP_SET_INVALID_ID;
+ } else {
+ req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+ req_max_sets->set.index =
+ ip_set_find_byname(req_max_sets->set.name);
+ if (req_max_sets->set.index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
goto done;
}
- req_get->name[IP_SET_MAXNAMELEN - 1] = '\0';
- req_get->id = -1;
- set = ip_set_get_byname(req_get->name, &req_get->id);
- if (set) {
- req_get->ref = set->ref - 1;
- ip_set_put(set);
- }
- res = copy_to_user(user, data, copylen);
+ }
+ req_max_sets->max_sets = ip_set_max;
+ req_max_sets->sets = 0;
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL)
+ req_max_sets->sets++;
+ }
+ goto copy;
+ }
+ case IP_SET_OP_LIST_SIZE:
+ case IP_SET_OP_SAVE_SIZE: {
+ struct ip_set_req_setnames *req_setnames
+ = (struct ip_set_req_setnames *) data;
+ struct ip_set_name_list *name_list;
+ struct ip_set *set;
+ ip_set_id_t i;
+ int used;
+
+ if (*len < sizeof(struct ip_set_req_setnames)) {
+ ip_set_printk("short LIST_SIZE (want >=%d, got %d)",
+ sizeof(struct ip_set_req_setnames), *len);
+ res = -EINVAL;
goto done;
}
- case IP_SET_OP_GETSET_BYID: {
- struct ip_set_req_get *req_get
- = (struct ip_set_req_get *) data;
- if (*len != sizeof(struct ip_set_req_get)) {
- ip_set_printk("short _BYID (want >=%d, got %d)",
- sizeof(struct ip_set_req_get), *len);
- res = -EINVAL;
+ req_setnames->size = 0;
+ used = sizeof(struct ip_set_req_setnames);
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] == NULL)
+ continue;
+ name_list = (struct ip_set_name_list *)
+ (data + used);
+ used += sizeof(struct ip_set_name_list);
+ if (used > copylen) {
+ res = -EAGAIN;
goto done;
}
- set = ip_set_get_byid(req_get->id);
- if (set) {
- req_get->ref = set->ref - 1;
- strncpy(req_get->name, set->name, IP_SET_MAXNAMELEN);
- ip_set_put(set);
- } else
- req_get->id = -1;
- res = copy_to_user(user, data, copylen);
+ set = ip_set_list[i];
+ /* Fill in index, name, etc. */
+ name_list->index = i;
+ name_list->id = set->id;
+ strncpy(name_list->name,
+ set->name,
+ IP_SET_MAXNAMELEN - 1);
+ strncpy(name_list->typename,
+ set->type->typename,
+ IP_SET_MAXNAMELEN - 1);
+ DP("filled %s of type %s, index %u\n",
+ name_list->name, name_list->typename,
+ name_list->index);
+ if (!(req_setnames->index == IP_SET_INVALID_ID
+ || req_setnames->index == i))
+ continue;
+ /* Update size */
+ switch (*op) {
+ case IP_SET_OP_LIST_SIZE: {
+ req_setnames->size += sizeof(struct ip_set_list)
+ + set->type->list_header_size(set)
+ + set->type->list_members_size(set);
+ FOREACH_HASH_DO(__set_hash_bindings_size_list,
+ i, &req_setnames->size);
+ break;
+ }
+ case IP_SET_OP_SAVE_SIZE: {
+ req_setnames->size += sizeof(struct ip_set_save)
+ + set->type->list_header_size(set)
+ + set->type->list_members_size(set);
+ FOREACH_HASH_DO(__set_hash_bindings_size_save,
+ i, &req_setnames->size);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ if (copylen != used) {
+ res = -EAGAIN;
goto done;
}
- default:
- ; /* Requests with set id: fall trought */
+ goto copy;
}
+ case IP_SET_OP_LIST: {
+ struct ip_set_req_list *req_list
+ = (struct ip_set_req_list *) data;
+ ip_set_id_t i;
+ int used;
- /* Requests with set id: */
- if (req_base->id < 0 || req_base->id >= max_sets) {
- res = -EINVAL;
- goto done;
+ if (*len < sizeof(struct ip_set_req_list)) {
+ ip_set_printk("short LIST (want >=%d, got %d)",
+ sizeof(struct ip_set_req_list), *len);
+ res = -EINVAL;
+ goto done;
+ }
+ index = req_list->index;
+ if (index != IP_SET_INVALID_ID
+ && ip_set_find_byindex(index) != index) {
+ res = -ENOENT;
+ goto done;
+ }
+ used = 0;
+ if (index == IP_SET_INVALID_ID) {
+ /* List all sets */
+ for (i = 0; i < ip_set_max && res == 0; i++) {
+ if (ip_set_list[i] != NULL)
+ res = ip_set_list_set(i, data, &used, *len);
+ }
+ } else {
+ /* List an individual set */
+ res = ip_set_list_set(index, data, &used, *len);
+ }
+ if (res != 0)
+ goto done;
+ else if (copylen != used) {
+ res = -EAGAIN;
+ goto done;
+ }
+ goto copy;
}
- set = ip_set_get_byid(req_base->id); /* Reference lock */
- if (!set) {
- res = -ENOENT;
- goto done;
- }
+ case IP_SET_OP_SAVE: {
+ struct ip_set_req_list *req_save
+ = (struct ip_set_req_list *) data;
+ ip_set_id_t i;
+ int used;
- DP("set %s (%d) (%u)", set->name, req_base->id, set->ref);
- req_std = (struct ip_set_req_std *) data;
- if (*len < sizeof(struct ip_set_req_std)) {
- ip_set_printk("short data in std request (want >%d, got %d)",
- sizeof(struct ip_set_req_std), *len);
- goto put_inval;
- } else if (req_std->level >= set->levels) {
- res = -ERANGE;
- goto put_set;
- }
-
- switch (req_base->op) {
- case IP_SET_OP_TEST_IP:{
- struct ip_set_req_test *req_test =
- (struct ip_set_req_test *) data;
-
- if (*len < sizeof(struct ip_set_req_test)) {
- ip_set_printk("short data in testip request (want >%d, got %d)",
- sizeof(struct ip_set_req_test), *len);
- res = -EINVAL;
- goto put_set;
- }
- req_test->reply = ip_set_testip(set,
- req_test->ip,
- req_test->level,
- data + sizeof(struct ip_set_req_test),
- *len - sizeof(struct ip_set_req_test));
-
- DP("test result: %i", req_test->reply);
- *len = copylen = sizeof(struct ip_set_req_test);
- goto put_copy;
+ if (*len < sizeof(struct ip_set_req_list)) {
+ ip_set_printk("short SAVE (want >=%d, got %d)",
+ sizeof(struct ip_set_req_list), *len);
+ res = -EINVAL;
+ goto done;
}
- case IP_SET_OP_LIST_HEADER_SIZE:
- case IP_SET_OP_LIST_MEMBERS_SIZE:
- case IP_SET_OP_LIST_CHILDSETS_SIZE: {
- struct ip_set_req_list *req_list =
- (struct ip_set_req_list *) data;
-
- if (*len != sizeof(struct ip_set_req_list)) {
- ip_set_printk("short LIST (want >=%d, got %d)",
- sizeof(struct ip_set_req_list),
- *len);
- goto put_inval;
+ index = req_save->index;
+ if (index != IP_SET_INVALID_ID
+ && ip_set_find_byindex(index) != index) {
+ res = -ENOENT;
+ goto done;
+ }
+ used = 0;
+ if (index == IP_SET_INVALID_ID) {
+ /* Save all sets */
+ for (i = 0; i < ip_set_max && res == 0; i++) {
+ if (ip_set_list[i] != NULL)
+ res = ip_set_save_set(i, data, &used, *len);
}
- res = ip_set_list_size(set,
- req_list->ip,
- req_list->level,
- &req_list->size,
- req_base->op);
- DP("SIZEfoo size=%d", req_list->size);
- if (res < 0)
- goto put_set; /* Error */
- goto put_copy;
+ } else {
+ /* Save an individual set */
+ res = ip_set_save_set(index, data, &used, *len);
}
- case IP_SET_OP_LIST_HEADER:
- case IP_SET_OP_LIST_MEMBERS:
- case IP_SET_OP_LIST_CHILDSETS:{
- DP("LISTfoo before len=%d", *len);
- res = ip_set_list_data(set,
- req_std->ip,
- req_std->level,
- data,
- len,
- req_base->op);
- DP("LISTfoo done len=%d", *len);
+ if (res == 0)
+ res = ip_set_save_bindings(index, data, &used, *len);
+
+ if (res != 0)
+ goto done;
+ else if (copylen != used) {
+ res = -EAGAIN;
+ goto done;
+ }
+ goto copy;
+ }
+ case IP_SET_OP_RESTORE: {
+ struct ip_set_req_setnames *req_restore
+ = (struct ip_set_req_setnames *) data;
+ int line;
- if (res < 0)
- goto put_set; /* Error */
-
- copylen = *len; /* Only copy the mem used */
- goto put_copy;
+ if (*len < sizeof(struct ip_set_req_setnames)
+ || *len != req_restore->size) {
+ ip_set_printk("invalid RESTORE (want =%d, got %d)",
+ req_restore->size, *len);
+ res = -EINVAL;
+ goto done;
}
- default:{
- DP("unknown op %d", req_base->op);
- ip_set_printk("obsolete - upgrade your ipset(8) utility.");
- goto put_inval;
+ line = ip_set_restore(data + sizeof(struct ip_set_req_setnames),
+ req_restore->size - sizeof(struct ip_set_req_setnames));
+ DP("ip_set_restore: %u", line);
+ if (line != 0) {
+ res = -EAGAIN;
+ req_restore->size = line;
+ copylen = sizeof(struct ip_set_req_setnames);
+ goto copy;
}
+ goto done;
+ }
+ default:
+ res = -EBADMSG;
+ goto done;
} /* end of switch(op) */
- put_copy:
- ip_set_put(set);
- DP("set %s (%u)", set->name, set->ref);
- res = copy_to_user(user, data, copylen);
- goto done;
- put_inval:
- res = -EINVAL;
- put_set:
- ip_set_put(set);
- DP("set %s (%u)", set->name, set->ref);
+ copy:
+ DP("set %s, copylen %u", index != IP_SET_INVALID_ID
+ && ip_set_list[index]
+ ? ip_set_list[index]->name
+ : ":all:", copylen);
+ if (res == 0)
+ res = copy_to_user(user, data, copylen);
+ else
+ copy_to_user(user, data, copylen);
+
done:
+ up(&ip_set_app_mutex);
vfree(data);
if (res > 0)
res = 0;
@@ -1444,27 +1923,47 @@
.use = 0
};
+static int max_sets, hash_size;
MODULE_PARM(max_sets, "i");
MODULE_PARM_DESC(max_sets, "maximal number of sets");
+MODULE_PARM(hash_size, "i");
+MODULE_PARM_DESC(hash_size, "hash size for bindings");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("module implementing core IP set support");
static int __init init(void)
{
int res;
+ ip_set_id_t i;
- if (max_sets <= 0)
- max_sets = CONFIG_IP_NF_SET_MAX;
- ip_set_list = vmalloc(sizeof(struct ip_set *) * max_sets);
+ get_random_bytes(&ip_set_hash_random, 4);
+ if (max_sets)
+ ip_set_max = max_sets;
+ ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max);
if (!ip_set_list) {
printk(KERN_ERR "Unable to create ip_set_list\n");
return -ENOMEM;
}
- memset(ip_set_list, 0, sizeof(struct ip_set *) * max_sets);
+ memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max);
+ if (hash_size)
+ ip_set_bindings_hash_size = hash_size;
+ ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size);
+ if (!ip_set_hash) {
+ printk(KERN_ERR "Unable to create ip_set_hash\n");
+ vfree(ip_set_list);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ip_set_bindings_hash_size; i++)
+ INIT_LIST_HEAD(&ip_set_hash[i]);
+
INIT_LIST_HEAD(&set_type_list);
res = nf_register_sockopt(&so_set);
if (res != 0) {
ip_set_printk("SO_SET registry failed: %d", res);
vfree(ip_set_list);
+ vfree(ip_set_hash);
return res;
}
return 0;
@@ -1472,17 +1971,18 @@
static void __exit fini(void)
{
+ /* There can't be any existing set or binding. Racy. */
nf_unregister_sockopt(&so_set);
vfree(ip_set_list);
+ vfree(ip_set_hash);
DP("these are the famous last words");
}
EXPORT_SYMBOL(ip_set_register_set_type);
EXPORT_SYMBOL(ip_set_unregister_set_type);
-EXPORT_SYMBOL(ip_set_list);
EXPORT_SYMBOL(ip_set_get_byname);
-EXPORT_SYMBOL(ip_set_get_byid);
+EXPORT_SYMBOL(ip_set_get_byindex);
EXPORT_SYMBOL(ip_set_put);
EXPORT_SYMBOL(ip_set_addip_kernel);
@@ -1491,4 +1991,3 @@
module_init(init);
module_exit(fini);
-MODULE_LICENSE("GPL");
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_iphash.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_iphash.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_iphash.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,18 +1,8 @@
-/* Copyright 2004 Jozsef Kadlecsik (kadlec at blackhole.kfki.hu)
+/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at 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
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
/* Kernel module implementing an ip hash set */
@@ -28,36 +18,59 @@
#include <asm/softirq.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
+#include <linux/random.h>
#include <net/ip.h>
#include <linux/netfilter_ipv4/ip_set_iphash.h>
#include <linux/netfilter_ipv4/ip_set_jhash.h>
+#include <linux/netfilter_ipv4/ip_set_prime.h>
-static inline ip_set_ip_t
-hash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip)
+static inline __u32
+jhash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip)
{
- return (jhash_1word(ip & map->netmask, map->initval) % map->hashsize);
+ return jhash_1word(ip, map->initval);
}
-static inline int
-__testip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+static inline __u32
+randhash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip)
{
- struct ip_set_iphash *map = (struct ip_set_iphash *) private;
+ return (1 + ip % map->prime);
+}
- *id = hash_ip(map, ip);
- return (map->members[*id] == ip);
+static inline __u32
+hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ __u32 jhash, randhash, id;
+ u_int16_t i;
+
+ *hash_ip = ip & map->netmask;
+ jhash = jhash_ip(map, *hash_ip);
+ randhash = randhash_ip(map, *hash_ip);
+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
+ set->name, NIPQUAD(ip), NIPQUAD(*hash_ip));
+
+ for (i = 0; i < map->probes; i++) {
+ id = (jhash + i * randhash) % map->hashsize;
+ DP("hash key: %u", id);
+ if (map->members[id] == *hash_ip)
+ return id;
+ else if (!map->members[id])
+ return UINT_MAX;
+ }
+ return UINT_MAX;
}
-static int
-matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+static inline int
+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
{
- return __testip(private, ip, id);
+ return (hash_id(set, ip, hash_ip) != UINT_MAX);
}
static int
-testip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+testip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
{
struct ip_set_req_iphash *req =
(struct ip_set_req_iphash *) data;
@@ -68,43 +81,45 @@
size);
return -EINVAL;
}
- return __testip(private, req->ip, id);
+ return __testip(set, req->ip, hash_ip);
}
static int
-testip_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+testip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
{
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
-
- return __testip(private,
+ return __testip(set,
ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr),
- id);
+ hash_ip);
}
static inline int
-__addip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id,
- u_int32_t flags)
+__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
{
- struct ip_set_iphash *map = (struct ip_set_iphash *) private;
+ __u32 jhash, randhash, probe;
+ u_int16_t i;
- *id = hash_ip(map, ip);
-
- if (map->members[*id] == ip)
- return -EEXIST;
-
- if (map->members[*id] != 0 && !(flags & IPSET_ADD_OVERWRITE))
- return -EADDRINUSE;
-
- map->members[*id] = ip;
- return 0;
+ *hash_ip = ip & map->netmask;
+ jhash = jhash_ip(map, *hash_ip);
+ randhash = randhash_ip(map, *hash_ip);
+
+ for (i = 0; i < map->probes; i++) {
+ probe = (jhash + i * randhash) % map->hashsize;
+ if (map->members[probe] == *hash_ip)
+ return -EEXIST;
+ if (!map->members[probe]) {
+ map->members[probe] = *hash_ip;
+ return 0;
+ }
+ }
+ /* Trigger rehashing */
+ return -EAGAIN;
}
static int
-addip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+addip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
{
struct ip_set_req_iphash *req =
(struct ip_set_req_iphash *) data;
@@ -115,43 +130,101 @@
size);
return -EINVAL;
}
- return __addip(private, req->ip, id, req->flags);
+ return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip);
}
static int
-addip_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+addip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
{
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
-
- return __addip(private,
+ return __addip((struct ip_set_iphash *) set->data,
ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr),
- id,
- flags);
+ hash_ip);
}
-static inline int
-__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+static int retry(struct ip_set *set)
{
- struct ip_set_iphash *map = (struct ip_set_iphash *) private;
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ ip_set_ip_t hash_ip, *members;
+ u_int32_t i, hashsize;
+ unsigned newbytes;
+ int res;
+ struct ip_set_iphash tmp = {
+ .hashsize = map->hashsize,
+ .probes = map->probes,
+ .resize = map->resize,
+ .netmask = map->netmask,
+ };
+
+ if (map->resize == 0)
+ return -ERANGE;
- *id = hash_ip(map, ip);
+ again:
+ res = 0;
+
+ /* Calculate new parameters */
+ get_random_bytes(&tmp.initval, 4);
+ hashsize = tmp.hashsize + (tmp.hashsize * map->resize)/100;
+ if (hashsize == tmp.hashsize)
+ hashsize++;
+ tmp.prime = make_prime(hashsize);
+
+ ip_set_printk("rehashing of set %s triggered: "
+ "hashsize grows from %u to %u",
+ set->name, tmp.hashsize, hashsize);
+ tmp.hashsize = hashsize;
+
+ newbytes = hashsize * sizeof(ip_set_ip_t);
+ tmp.members = vmalloc(newbytes);
+ if (!tmp.members) {
+ DP("out of memory for %d bytes", newbytes);
+ return -ENOMEM;
+ }
+ memset(tmp.members, 0, newbytes);
+
+ write_lock_bh(&set->lock);
+ map = (struct ip_set_iphash *) set->data; /* Play safe */
+ for (i = 0; i < map->hashsize && res == 0; i++) {
+ if (map->members[i])
+ res = __addip(&tmp, map->members[i], &hash_ip);
+ }
+ if (res) {
+ /* Failure, try again */
+ vfree(tmp.members);
+ write_unlock_bh(&set->lock);
+ goto again;
+ }
+
+ /* Success at resizing! */
+ members = map->members;
+ map->initval = tmp.initval;
+ map->prime = tmp.prime;
+ map->hashsize = tmp.hashsize;
+ map->members = tmp.members;
+ write_unlock_bh(&set->lock);
- if (map->members[*id] == 0)
- return -EEXIST;
+ vfree(members);
- if (map->members[*id] != ip)
- return -EADDRINUSE;
+ return 0;
+}
- map->members[*id] = 0;
+static inline int
+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ ip_set_ip_t id = hash_id(set, ip, hash_ip);
+
+ if (id == UINT_MAX)
+ return -EEXIST;
+
+ map->members[id] = 0;
return 0;
}
static int
-delip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+delip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
{
struct ip_set_req_iphash *req =
(struct ip_set_req_iphash *) data;
@@ -162,25 +235,22 @@
size);
return -EINVAL;
}
- return __delip(private, req->ip, id);
+ return __delip(set, req->ip, hash_ip);
}
static int
-delip_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+delip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
{
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
-
- return __delip(private,
+ return __delip(set,
ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr),
- id);
+ hash_ip);
}
-static int create(struct ip_set_private **private, const void *data, size_t size)
+static int create(struct ip_set *set, const void *data, size_t size)
{
- int newbytes;
+ unsigned newbytes;
struct ip_set_req_iphash_create *req =
(struct ip_set_req_iphash_create *) data;
struct ip_set_iphash *map;
@@ -192,15 +262,9 @@
return -EINVAL;
}
- if (req->hashsize > MAX_RANGE) {
- ip_set_printk("hashsize too big (max %d)",
- MAX_RANGE);
- return -ERANGE;
- }
-
if (req->hashsize < 1) {
ip_set_printk("hashsize too small");
- return -ERANGE;
+ return -ENOEXEC;
}
map = kmalloc(sizeof(struct ip_set_iphash), GFP_KERNEL);
@@ -209,8 +273,11 @@
sizeof(struct ip_set_iphash));
return -ENOMEM;
}
- map->initval = req->initval;
+ get_random_bytes(&map->initval, 4);
+ map->prime = make_prime(req->hashsize);
map->hashsize = req->hashsize;
+ map->probes = req->probes;
+ map->resize = req->resize;
map->netmask = req->netmask;
newbytes = map->hashsize * sizeof(ip_set_ip_t);
map->members = vmalloc(newbytes);
@@ -221,65 +288,58 @@
}
memset(map->members, 0, newbytes);
- *private = (struct ip_set_private *) map;
+ set->data = map;
return 0;
}
-static void destroy(struct ip_set_private **private)
+static void destroy(struct ip_set *set)
{
- struct ip_set_iphash *map = (struct ip_set_iphash *) *private;
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
vfree(map->members);
kfree(map);
- *private = NULL;
+ set->data = NULL;
}
-static void flush(struct ip_set_private *private)
+static void flush(struct ip_set *set)
{
- struct ip_set_iphash *map = (struct ip_set_iphash *) private;
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
memset(map->members, 0, map->hashsize * sizeof(ip_set_ip_t));
}
-static int list_header_size(const struct ip_set_private *private)
+static int list_header_size(const struct ip_set *set)
{
return sizeof(struct ip_set_req_iphash_create);
}
-static void list_header(const struct ip_set_private *private, void *data)
+static void list_header(const struct ip_set *set, void *data)
{
- struct ip_set_iphash *map = (struct ip_set_iphash *) private;
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
struct ip_set_req_iphash_create *header =
(struct ip_set_req_iphash_create *) data;
- header->initval = map->initval;
header->hashsize = map->hashsize;
+ header->probes = map->probes;
+ header->resize = map->resize;
header->netmask = map->netmask;
}
-static int list_members_size(const struct ip_set_private *private)
+static int list_members_size(const struct ip_set *set)
{
- struct ip_set_iphash *map = (struct ip_set_iphash *) private;
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
return (map->hashsize * sizeof(ip_set_ip_t));
}
-static void list_members(const struct ip_set_private *private, void *data)
+static void list_members(const struct ip_set *set, void *data)
{
- struct ip_set_iphash *map = (struct ip_set_iphash *) private;
-
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
int bytes = map->hashsize * sizeof(ip_set_ip_t);
memcpy(data, map->members, bytes);
}
-static ip_set_ip_t sizeid(const struct ip_set_private *private)
-{
- struct ip_set_iphash *map = (struct ip_set_iphash *) private;
-
- return (map->hashsize);
-}
-
static struct ip_set_type ip_set_iphash = {
.typename = SETTYPE_NAME,
.typecode = IPSET_TYPE_IP,
@@ -290,19 +350,22 @@
.reqsize = sizeof(struct ip_set_req_iphash),
.addip = &addip,
.addip_kernel = &addip_kernel,
+ .retry = &retry,
.delip = &delip,
.delip_kernel = &delip_kernel,
- .matchip = &matchip,
.testip = &testip,
.testip_kernel = &testip_kernel,
.list_header_size = &list_header_size,
.list_header = &list_header,
.list_members_size = &list_members_size,
.list_members = &list_members,
- .sizeid = &sizeid,
.me = THIS_MODULE,
};
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("iphash type of IP sets");
+
static int __init init(void)
{
return ip_set_register_set_type(&ip_set_iphash);
@@ -316,4 +379,3 @@
module_init(init);
module_exit(fini);
-MODULE_LICENSE("GPL");
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_ipmap.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_ipmap.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_ipmap.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,20 +1,10 @@
-/* Copyright 2000-2004 Joakim Axelsson (gozem at linux.nu)
- * Patrick Schaaf (bof at bof.de)
- * Jozsef Kadlecsik (kadlec at blackhole.kfki.hu)
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at 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
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the single bitmap type */
@@ -35,31 +25,27 @@
static inline ip_set_ip_t
ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip)
{
- return ((ip & map->netmask) - map->first_ip)/map->hosts;
+ return (ip - map->first_ip)/map->hosts;
}
static inline int
-__testip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) private;
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
if (ip < map->first_ip || ip > map->last_ip)
return -ERANGE;
- *id = ip_to_id(map, ip);
- return !!test_bit(*id, map->members);
+ *hash_ip = ip & map->netmask;
+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
+ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip));
+ return !!test_bit(ip_to_id(map, *hash_ip), map->members);
}
static int
-matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+testip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
{
- return __testip(private, ip, id);
-}
-
-static int
-testip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
-{
struct ip_set_req_ipmap *req =
(struct ip_set_req_ipmap *) data;
@@ -69,49 +55,48 @@
size);
return -EINVAL;
}
- return __testip(private, req->ip, id);
+ return __testip(set, req->ip, hash_ip);
}
static int
-testip_kernel(struct ip_set_private *private,
+testip_kernel(struct ip_set *set,
const struct sk_buff *skb,
u_int32_t flags,
- ip_set_ip_t *id)
+ ip_set_ip_t *hash_ip)
{
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
+ int res;
- DP("flags: %u (%s) ip %u.%u.%u.%u", flags,
+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u",
flags & IPSET_SRC ? "SRC" : "DST",
- NIPQUAD(skb->nh.iph->saddr));
- DP("flags: %u (%s) ip %u.%u.%u.%u", flags,
- flags & IPSET_SRC ? "SRC" : "DST",
+ NIPQUAD(skb->nh.iph->saddr),
NIPQUAD(skb->nh.iph->daddr));
- return __testip(private,
+ res = __testip(set,
ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr),
- id);
+ hash_ip);
+ return (res < 0 ? 0 : res);
}
static inline int
-__addip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) private;
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
if (ip < map->first_ip || ip > map->last_ip)
return -ERANGE;
- *id = ip_to_id(map, ip);
- if (test_and_set_bit(*id, map->members))
+ *hash_ip = ip & map->netmask;
+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip));
+ if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members))
return -EEXIST;
return 0;
}
static int
-addip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+addip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
{
struct ip_set_req_ipmap *req =
(struct ip_set_req_ipmap *) data;
@@ -122,41 +107,39 @@
size);
return -EINVAL;
}
- DP("%u.%u.%u.%u", NIPQUAD(req->ip));
- return __addip(private, req->ip, id);
+ DP("%u.%u.%u.%u", HIPQUAD(req->ip));
+ return __addip(set, req->ip, hash_ip);
}
static int
-addip_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+addip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
{
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
-
- return __addip(private,
+ return __addip(set,
ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr),
- id);
+ hash_ip);
}
static inline int
-__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) private;
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
if (ip < map->first_ip || ip > map->last_ip)
return -ERANGE;
- *id = ip_to_id(map, ip);
- if (!test_and_clear_bit(*id, map->members))
+ *hash_ip = ip & map->netmask;
+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip));
+ if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members))
return -EEXIST;
return 0;
}
static int
-delip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+delip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
{
struct ip_set_req_ipmap *req =
(struct ip_set_req_ipmap *) data;
@@ -167,23 +150,20 @@
size);
return -EINVAL;
}
- return __delip(private, req->ip, id);
+ return __delip(set, req->ip, hash_ip);
}
static int
-delip_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+delip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
{
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
-
- return __delip(private,
+ return __delip(set,
ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr),
- id);
+ hash_ip);
}
-static int create(struct ip_set_private **private, const void *data, size_t size)
+static int create(struct ip_set *set, const void *data, size_t size)
{
int newbytes;
struct ip_set_req_ipmap_create *req =
@@ -197,17 +177,18 @@
return -EINVAL;
}
- DP("from 0x%08x to 0x%08x", req->from, req->to);
+ DP("from %u.%u.%u.%u to %u.%u.%u.%u",
+ HIPQUAD(req->from), HIPQUAD(req->to));
if (req->from > req->to) {
DP("bad ip range");
- return -EINVAL;
+ return -ENOEXEC;
}
if (req->to - req->from > MAX_RANGE) {
ip_set_printk("range too big (max %d addresses)",
MAX_RANGE);
- return -ERANGE;
+ return -ENOEXEC;
}
map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL);
@@ -233,13 +214,12 @@
netmask_bits = mask_to_bits(map->netmask);
if (!mask || netmask_bits <= mask_bits)
- return -EINVAL;
+ return -ENOEXEC;
map->hosts = 2 << (32 - netmask_bits - 1);
map->sizeid = 2 << (netmask_bits - mask_bits - 1);
}
newbytes = bitmap_bytes(0, map->sizeid - 1);
- DP("%x %x %i", map->first_ip, map->last_ip, newbytes);
map->members = kmalloc(newbytes, GFP_KERNEL);
if (!map->members) {
DP("out of memory for %d bytes", newbytes);
@@ -248,67 +228,57 @@
}
memset(map->members, 0, newbytes);
- *private = (struct ip_set_private *) map;
+ set->data = map;
return 0;
}
-static void destroy(struct ip_set_private **private)
+static void destroy(struct ip_set *set)
{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) *private;
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
kfree(map->members);
kfree(map);
- *private = NULL;
+ set->data = NULL;
}
-static void flush(struct ip_set_private *private)
+static void flush(struct ip_set *set)
{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) private;
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1));
}
-static int list_header_size(const struct ip_set_private *private)
+static int list_header_size(const struct ip_set *set)
{
return sizeof(struct ip_set_req_ipmap_create);
}
-static void list_header(const struct ip_set_private *private, void *data)
+static void list_header(const struct ip_set *set, void *data)
{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) private;
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
struct ip_set_req_ipmap_create *header =
(struct ip_set_req_ipmap_create *) data;
- DP("list_header %x %x", map->first_ip, map->last_ip);
-
header->from = map->first_ip;
header->to = map->last_ip;
header->netmask = map->netmask;
}
-static int list_members_size(const struct ip_set_private *private)
+static int list_members_size(const struct ip_set *set)
{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) private;
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
return bitmap_bytes(0, map->sizeid - 1);
}
-static void list_members(const struct ip_set_private *private, void *data)
+static void list_members(const struct ip_set *set, void *data)
{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) private;
-
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
int bytes = bitmap_bytes(0, map->sizeid - 1);
memcpy(data, map->members, bytes);
}
-static ip_set_ip_t sizeid(const struct ip_set_private *private)
-{
- struct ip_set_ipmap *map = (struct ip_set_ipmap *) private;
-
- return (map->sizeid);
-}
-
static struct ip_set_type ip_set_ipmap = {
.typename = SETTYPE_NAME,
.typecode = IPSET_TYPE_IP,
@@ -321,17 +291,19 @@
.addip_kernel = &addip_kernel,
.delip = &delip,
.delip_kernel = &delip_kernel,
- .matchip = &matchip,
.testip = &testip,
.testip_kernel = &testip_kernel,
.list_header_size = &list_header_size,
.list_header = &list_header,
.list_members_size = &list_members_size,
.list_members = &list_members,
- .sizeid = &sizeid,
.me = THIS_MODULE,
};
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("ipmap type of IP sets");
+
static int __init init(void)
{
return ip_set_register_set_type(&ip_set_ipmap);
@@ -345,4 +317,3 @@
module_init(init);
module_exit(fini);
-MODULE_LICENSE("GPL");
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_macipmap.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_macipmap.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_macipmap.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,20 +1,11 @@
-/* Copyright 2000-2004 Joakim Axelsson (gozem at linux.nu)
- * Patrick Schaaf (bof at bof.de)
- * Martin Josefsson (gandalf at wlug.westbo.se)
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Martin Josefsson <gandalf at wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at 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
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
/* Kernel module implementing an IP set type: the macipmap type */
@@ -35,30 +26,12 @@
#include <linux/netfilter_ipv4/ip_set_macipmap.h>
static int
-matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip)
{
- struct ip_set_macipmap *map = (struct ip_set_macipmap *) private;
- struct ip_set_macip *table =
- (struct ip_set_macip *) map->members;
-
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
+ struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data;
+ struct ip_set_macip *table = (struct ip_set_macip *) map->members;
+ struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data;
- *id = ip - map->first_ip;
- return !!test_bit(IPSET_MACIP_ISSET, &table[*id].flags);
-}
-
-static int
-testip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
-{
- struct ip_set_macipmap *map = (struct ip_set_macipmap *) private;
- struct ip_set_macip *table =
- (struct ip_set_macip *) map->members;
-
- struct ip_set_req_macipmap *req =
- (struct ip_set_req_macipmap *) data;
-
if (size != sizeof(struct ip_set_req_macipmap)) {
ip_set_printk("data length wrong (want %d, have %d)",
sizeof(struct ip_set_req_macipmap),
@@ -69,12 +42,15 @@
if (req->ip < map->first_ip || req->ip > map->last_ip)
return -ERANGE;
- *id = req->ip - map->first_ip;
- if (test_bit(IPSET_MACIP_ISSET, &table[*id].flags)) {
+ *hash_ip = req->ip;
+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
+ set->name, NIPQUAD(req->ip), NIPQUAD(*hash_ip));
+ if (test_bit(IPSET_MACIP_ISSET,
+ &table[req->ip - map->first_ip].flags)) {
/* Is mac pointer valid?
* If so, compare... */
return (memcmp(req->ethernet,
- &table[*id].ethernet,
+ &table[req->ip - map->first_ip].ethernet,
ETH_ALEN) == 0);
} else {
return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0);
@@ -82,32 +58,35 @@
}
static int
-testip_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+testip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
{
struct ip_set_macipmap *map =
- (struct ip_set_macipmap *) private;
+ (struct ip_set_macipmap *) set->data;
struct ip_set_macip *table =
(struct ip_set_macip *) map->members;
ip_set_ip_t ip;
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
-
ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr);
+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u",
+ flags & IPSET_SRC ? "SRC" : "DST",
+ NIPQUAD(skb->nh.iph->saddr),
+ NIPQUAD(skb->nh.iph->daddr));
if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
+ return 0;
- *id = ip - map->first_ip;
- if (test_bit(IPSET_MACIP_ISSET, &table[*id].flags)) {
+ *hash_ip = ip;
+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
+ set->name, NIPQUAD(ip), NIPQUAD(*hash_ip));
+ if (test_bit(IPSET_MACIP_ISSET, &table[ip - map->first_ip].flags)) {
/* Is mac pointer valid?
* If so, compare... */
return (skb->mac.raw >= skb->head
&& (skb->mac.raw + ETH_HLEN) <= skb->data
&& (memcmp(skb->mac.ethernet->h_source,
- &table[*id].ethernet,
+ &table[ip - map->first_ip].ethernet,
ETH_ALEN) == 0));
} else {
return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0);
@@ -116,11 +95,11 @@
/* returns 0 on success */
static inline int
-__addip(struct ip_set_private *private,
- ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *id)
+__addip(struct ip_set *set,
+ ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip)
{
struct ip_set_macipmap *map =
- (struct ip_set_macipmap *) private;
+ (struct ip_set_macipmap *) set->data;
struct ip_set_macip *table =
(struct ip_set_macip *) map->members;
@@ -130,14 +109,15 @@
&table[ip - map->first_ip].flags))
return -EEXIST;
- *id = ip - map->first_ip;
- memcpy(&table[*id].ethernet, ethernet, ETH_ALEN);
+ *hash_ip = ip;
+ DP("%u.%u.%u.%u, %u.%u.%u.%u", NIPQUAD(ip), NIPQUAD(*hash_ip));
+ memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN);
return 0;
}
static int
-addip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+addip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
{
struct ip_set_req_macipmap *req =
(struct ip_set_req_macipmap *) data;
@@ -148,18 +128,15 @@
size);
return -EINVAL;
}
- return __addip(private, req->ip, req->ethernet, id);
+ return __addip(set, req->ip, req->ethernet, hash_ip);
}
static int
-addip_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+addip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
{
ip_set_ip_t ip;
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
-
ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr);
@@ -167,14 +144,14 @@
&& (skb->mac.raw + ETH_HLEN) <= skb->data))
return -EINVAL;
- return __addip(private, ip, skb->mac.ethernet->h_source, id);
+ return __addip(set, ip, skb->mac.ethernet->h_source, hash_ip);
}
static inline int
-__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id)
+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
{
struct ip_set_macipmap *map =
- (struct ip_set_macipmap *) private;
+ (struct ip_set_macipmap *) set->data;
struct ip_set_macip *table =
(struct ip_set_macip *) map->members;
@@ -184,13 +161,14 @@
&table[ip - map->first_ip].flags))
return -EEXIST;
- *id = ip - map->first_ip;
+ *hash_ip = ip;
+ DP("%u.%u.%u.%u, %u.%u.%u.%u", NIPQUAD(ip), NIPQUAD(*hash_ip));
return 0;
}
static int
-delip(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+delip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
{
struct ip_set_req_macipmap *req =
(struct ip_set_req_macipmap *) data;
@@ -201,23 +179,20 @@
size);
return -EINVAL;
}
- return __delip(private, req->ip, id);
+ return __delip(set, req->ip, hash_ip);
}
static int
-delip_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+delip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
{
- if (!(flags & IPSET_TYPE_IP))
- return -EINVAL;
-
- return __delip(private,
+ return __delip(set,
ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
: skb->nh.iph->daddr),
- id);
+ hash_ip);
}
-static int create(struct ip_set_private **private, const void *data, size_t size)
+static int create(struct ip_set *set, const void *data, size_t size)
{
int newbytes;
struct ip_set_req_macipmap_create *req =
@@ -231,17 +206,18 @@
return -EINVAL;
}
- DP("from 0x%08x to 0x%08x", req->from, req->to);
+ DP("from %u.%u.%u.%u to %u.%u.%u.%u",
+ NIPQUAD(req->from), NIPQUAD(req->to));
if (req->from > req->to) {
DP("bad ip range");
- return -EINVAL;
+ return -ENOEXEC;
}
if (req->to - req->from > MAX_RANGE) {
ip_set_printk("range too big (max %d addresses)",
MAX_RANGE);
- return -ERANGE;
+ return -ENOEXEC;
}
map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL);
@@ -262,38 +238,38 @@
}
memset(map->members, 0, newbytes);
- *private = (struct ip_set_private *) map;
+ set->data = map;
return 0;
}
-static void destroy(struct ip_set_private **private)
+static void destroy(struct ip_set *set)
{
struct ip_set_macipmap *map =
- (struct ip_set_macipmap *) *private;
+ (struct ip_set_macipmap *) set->data;
vfree(map->members);
kfree(map);
- *private = NULL;
+ set->data = NULL;
}
-static void flush(struct ip_set_private *private)
+static void flush(struct ip_set *set)
{
struct ip_set_macipmap *map =
- (struct ip_set_macipmap *) private;
+ (struct ip_set_macipmap *) set->data;
memset(map->members, 0, (map->last_ip - map->first_ip)
* sizeof(struct ip_set_macip));
}
-static int list_header_size(const struct ip_set_private *private)
+static int list_header_size(const struct ip_set *set)
{
return sizeof(struct ip_set_req_macipmap_create);
}
-static void list_header(const struct ip_set_private *private, void *data)
+static void list_header(const struct ip_set *set, void *data)
{
struct ip_set_macipmap *map =
- (struct ip_set_macipmap *) private;
+ (struct ip_set_macipmap *) set->data;
struct ip_set_req_macipmap_create *header =
(struct ip_set_req_macipmap_create *) data;
@@ -305,19 +281,19 @@
header->flags = map->flags;
}
-static int list_members_size(const struct ip_set_private *private)
+static int list_members_size(const struct ip_set *set)
{
struct ip_set_macipmap *map =
- (struct ip_set_macipmap *) private;
+ (struct ip_set_macipmap *) set->data;
return (map->last_ip
- map->first_ip + 1) * sizeof(struct ip_set_macip);
}
-static void list_members(const struct ip_set_private *private, void *data)
+static void list_members(const struct ip_set *set, void *data)
{
struct ip_set_macipmap *map =
- (struct ip_set_macipmap *) private;
+ (struct ip_set_macipmap *) set->data;
int bytes = (map->last_ip -
- map->first_ip + 1) * sizeof(struct ip_set_macip);
@@ -325,13 +301,6 @@
memcpy(data, map->members, bytes);
}
-static ip_set_ip_t sizeid(const struct ip_set_private *private)
-{
- struct ip_set_macipmap *map = (struct ip_set_macipmap *) private;
-
- return (map->last_ip - map->first_ip + 1);
-}
-
static struct ip_set_type ip_set_macipmap = {
.typename = SETTYPE_NAME,
.typecode = IPSET_TYPE_IP,
@@ -344,17 +313,19 @@
.addip_kernel = &addip_kernel,
.delip = &delip,
.delip_kernel = &delip_kernel,
- .matchip = &matchip,
.testip = &testip,
.testip_kernel = &testip_kernel,
.list_header_size = &list_header_size,
.list_header = &list_header,
.list_members_size = &list_members_size,
.list_members = &list_members,
- .sizeid = &sizeid,
.me = THIS_MODULE,
};
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("macipmap type of IP sets");
+
static int __init init(void)
{
return ip_set_register_set_type(&ip_set_macipmap);
@@ -368,4 +339,3 @@
module_init(init);
module_exit(fini);
-MODULE_LICENSE("GPL");
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_portmap.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_portmap.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ip_set_portmap.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,20 +1,8 @@
-/* Copyright 2004 Jozsef Kadlecsik (kadlec at blackhole.kfki.hu)
+/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
*
- * Based on ip_set_ipmap.c
- *
- * 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
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
/* Kernel module implementing a port set type as a bitmap */
@@ -48,10 +36,10 @@
/* See comments at tcp_match in ip_tables.c */
if (offset != 0
- || (offset == 0
- && (skb->len - iph->ihl * 4) < sizeof(struct tcphdr)))
+ || (offset == 0
+ && (skb->len - iph->ihl * 4) < sizeof(struct tcphdr)))
return INVALID_PORT;
-
+
return ntohs(flags & IPSET_SRC ?
tcph->source : tcph->dest);
}
@@ -59,8 +47,8 @@
struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl);
if (offset != 0
- || (offset == 0
- && (skb->len - iph->ihl * 4) < sizeof(struct udphdr)))
+ || (offset == 0
+ && (skb->len - iph->ihl * 4) < sizeof(struct udphdr)))
return INVALID_PORT;
return ntohs(flags & IPSET_SRC ?
@@ -72,27 +60,22 @@
}
static inline int
-__testport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id)
+__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port)
{
- struct ip_set_portmap *map = (struct ip_set_portmap *) private;
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
if (port < map->first_port || port > map->last_port)
return -ERANGE;
- *id = port - map->first_port;
- return !!test_bit(*id, map->members);
+ *hash_port = port;
+ DP("set: %s, port:%u, %u", set->name, port, *hash_port);
+ return !!test_bit(port - map->first_port, map->members);
}
static int
-matchport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id)
+testport(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_port)
{
- return __testport(private, port, id);
-}
-
-static int
-testport(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
-{
struct ip_set_req_portmap *req =
(struct ip_set_req_portmap *) data;
@@ -102,47 +85,43 @@
size);
return -EINVAL;
}
- return __testport(private, req->port, id);
+ return __testport(set, req->port, hash_port);
}
static int
-testport_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+testport_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_port)
{
- ip_set_ip_t port;
-
- if (!(flags & IPSET_TYPE_PORT))
- return -EINVAL;
+ int res;
+ ip_set_ip_t port = get_port(skb, flags);
- port = get_port(skb, flags);
- DP("flags %u %s port %u",
- flags,
- flags & IPSET_SRC ? "SRC" : "DST",
- port);
-
+ DP("flag %s port %u", flags & IPSET_SRC ? "SRC" : "DST", port);
if (port == INVALID_PORT)
- return -EINVAL;
+ return 0;
- return __testport(private, port, id);
+ res = __testport(set, port, hash_port);
+
+ return (res < 0 ? 0 : res);
}
static inline int
-__addport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id)
+__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port)
{
- struct ip_set_portmap *map = (struct ip_set_portmap *) private;
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
if (port < map->first_port || port > map->last_port)
return -ERANGE;
if (test_and_set_bit(port - map->first_port, map->members))
return -EEXIST;
- *id = port - map->first_port;
+ *hash_port = port;
+ DP("port %u", port);
return 0;
}
static int
-addport(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+addport(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_port)
{
struct ip_set_req_portmap *req =
(struct ip_set_req_portmap *) data;
@@ -153,43 +132,39 @@
size);
return -EINVAL;
}
- return __addport(private, req->port, id);
+ return __addport(set, req->port, hash_port);
}
static int
-addport_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+addport_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_port)
{
- ip_set_ip_t port;
+ ip_set_ip_t port = get_port(skb, flags);
- if (!(flags & IPSET_TYPE_PORT))
- return -EINVAL;
-
- port = get_port(skb, flags);
-
if (port == INVALID_PORT)
return -EINVAL;
- return __addport(private, port, id);
+ return __addport(set, port, hash_port);
}
static inline int
-__delport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id)
+__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port)
{
- struct ip_set_portmap *map = (struct ip_set_portmap *) private;
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
if (port < map->first_port || port > map->last_port)
return -ERANGE;
if (!test_and_clear_bit(port - map->first_port, map->members))
return -EEXIST;
- *id = port - map->first_port;
+ *hash_port = port;
+ DP("port %u", port);
return 0;
}
static int
-delport(struct ip_set_private *private, const void *data, size_t size,
- ip_set_ip_t *id)
+delport(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_port)
{
struct ip_set_req_portmap *req =
(struct ip_set_req_portmap *) data;
@@ -200,27 +175,22 @@
size);
return -EINVAL;
}
- return __delport(private, req->port, id);
+ return __delport(set, req->port, hash_port);
}
static int
-delport_kernel(struct ip_set_private *private, const struct sk_buff *skb,
- u_int32_t flags, ip_set_ip_t *id)
+delport_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_port)
{
- ip_set_ip_t port;
+ ip_set_ip_t port = get_port(skb, flags);
- if (!(flags & IPSET_TYPE_PORT))
- return -EINVAL;
-
- port = get_port(skb, flags);
-
if (port == INVALID_PORT)
return -EINVAL;
- return __delport(private, port, id);
+ return __delport(set, port, hash_port);
}
-static int create(struct ip_set_private **private, const void *data, size_t size)
+static int create(struct ip_set *set, const void *data, size_t size)
{
int newbytes;
struct ip_set_req_portmap_create *req =
@@ -234,17 +204,17 @@
return -EINVAL;
}
- DP("from 0x%08x to 0x%08x", req->from, req->to);
+ DP("from %u to %u", req->from, req->to);
if (req->from > req->to) {
DP("bad port range");
- return -EINVAL;
+ return -ENOEXEC;
}
if (req->to - req->from > MAX_RANGE) {
ip_set_printk("range too big (max %d ports)",
MAX_RANGE);
- return -ERANGE;
+ return -ENOEXEC;
}
map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL);
@@ -264,66 +234,58 @@
}
memset(map->members, 0, newbytes);
- *private = (struct ip_set_private *) map;
+ set->data = map;
return 0;
}
-static void destroy(struct ip_set_private **private)
+static void destroy(struct ip_set *set)
{
- struct ip_set_portmap *map = (struct ip_set_portmap *) *private;
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
kfree(map->members);
kfree(map);
- *private = NULL;
+ set->data = NULL;
}
-static void flush(struct ip_set_private *private)
+static void flush(struct ip_set *set)
{
- struct ip_set_portmap *map = (struct ip_set_portmap *) private;
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port));
}
-static int list_header_size(const struct ip_set_private *private)
+static int list_header_size(const struct ip_set *set)
{
return sizeof(struct ip_set_req_portmap_create);
}
-static void list_header(const struct ip_set_private *private, void *data)
+static void list_header(const struct ip_set *set, void *data)
{
- struct ip_set_portmap *map = (struct ip_set_portmap *) private;
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
struct ip_set_req_portmap_create *header =
(struct ip_set_req_portmap_create *) data;
- DP("list_header %x %x", map->first_port, map->last_port);
+ DP("list_header %u %u", map->first_port, map->last_port);
header->from = map->first_port;
header->to = map->last_port;
}
-static int list_members_size(const struct ip_set_private *private)
+static int list_members_size(const struct ip_set *set)
{
- struct ip_set_portmap *map = (struct ip_set_portmap *) private;
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
return bitmap_bytes(map->first_port, map->last_port);
}
-static void list_members(const struct ip_set_private *private, void *data)
+static void list_members(const struct ip_set *set, void *data)
{
- struct ip_set_portmap *map = (struct ip_set_portmap *) private;
-
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
int bytes = bitmap_bytes(map->first_port, map->last_port);
memcpy(data, map->members, bytes);
}
-static ip_set_ip_t sizeid(const struct ip_set_private *private)
-{
- struct ip_set_portmap *map = (struct ip_set_portmap *) private;
-
- return (map->last_port - map->first_port + 1);
-}
-
static struct ip_set_type ip_set_portmap = {
.typename = SETTYPE_NAME,
.typecode = IPSET_TYPE_PORT,
@@ -336,17 +298,19 @@
.addip_kernel = &addport_kernel,
.delip = &delport,
.delip_kernel = &delport_kernel,
- .matchip = &matchport,
.testip = &testport,
.testip_kernel = &testport_kernel,
.list_header_size = &list_header_size,
.list_header = &list_header,
.list_members_size = &list_members_size,
.list_members = &list_members,
- .sizeid = &sizeid,
.me = THIS_MODULE,
};
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("portmap type of IP sets");
+
static int __init init(void)
{
return ip_set_register_set_type(&ip_set_portmap);
@@ -360,4 +324,3 @@
module_init(init);
module_exit(fini);
-MODULE_LICENSE("GPL");
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ipt_SET.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ipt_SET.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ipt_SET.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,12 +1,15 @@
-/* ipt_SET.c - netfilter target to manipulate IP sets
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Martin Josefsson <gandalf at wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
*
- * This target can be used almost everywhere. It acts on some specified
- * IP set, adding or deleting some IP addresses/ports in the set.
- * The addresses/ports can be either the source, or destination
- * of the packet under inspection.
- *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
+/* ipt_SET.c - netfilter target to manipulate IP sets */
+
#include <linux/types.h>
#include <linux/ip.h>
#include <linux/timer.h>
@@ -31,18 +34,14 @@
{
const struct ipt_set_info_target *info = targinfo;
- if (info->add_set.id >= 0)
- ip_set_addip_kernel(ip_set_list[info->add_set.id],
+ if (info->add_set.index != IP_SET_INVALID_ID)
+ ip_set_addip_kernel(info->add_set.index,
*pskb,
- info->add_set.flags,
- info->add_set.set_level,
- info->add_set.ip_level);
- if (info->del_set.id >= 0)
- ip_set_delip_kernel(ip_set_list[info->del_set.id],
+ info->add_set.flags);
+ if (info->del_set.index != IP_SET_INVALID_ID)
+ ip_set_delip_kernel(info->del_set.index,
*pskb,
- info->del_set.flags,
- info->del_set.set_level,
- info->del_set.ip_level);
+ info->del_set.flags);
return IPT_CONTINUE;
}
@@ -53,26 +52,32 @@
void *targinfo,
unsigned int targinfosize, unsigned int hook_mask)
{
- struct ipt_set_info_target *info = targinfo;
+ struct ipt_set_info_target *info =
+ (struct ipt_set_info_target *) targinfo;
+ ip_set_id_t index;
if (targinfosize != IPT_ALIGN(sizeof(*info))) {
DP("bad target info size %u", targinfosize);
return 0;
}
- if (info->add_set.id >= 0
- && !ip_set_get_byid(info->add_set.id)) {
- ip_set_printk("cannot verify add_set id %i as target",
- info->add_set.id);
- return 0; /* error */
+ if (info->add_set.index != IP_SET_INVALID_ID) {
+ index = ip_set_get_byindex(info->add_set.index);
+ if (index == IP_SET_INVALID_ID) {
+ ip_set_printk("cannot find add_set index %u as target",
+ info->add_set.index);
+ return 0; /* error */
+ }
}
- if (info->del_set.id >= 0
- && !ip_set_get_byid(info->del_set.id)) {
- ip_set_printk("cannot verify del_set id %i as target",
- info->del_set.id);
- return 0; /* error */
+
+ if (info->del_set.index != IP_SET_INVALID_ID) {
+ index = ip_set_get_byindex(info->del_set.index);
+ if (index == IP_SET_INVALID_ID) {
+ ip_set_printk("cannot find del_set index %u as target",
+ info->del_set.index);
+ return 0; /* error */
+ }
}
- DP("checkentry OK");
return 1;
}
@@ -86,10 +91,10 @@
return;
}
- if (info->add_set.id >= 0)
- ip_set_put(ip_set_list[info->add_set.id]);
- if (info->del_set.id >= 0)
- ip_set_put(ip_set_list[info->del_set.id]);
+ if (info->add_set.index != IP_SET_INVALID_ID)
+ ip_set_put(info->add_set.index);
+ if (info->del_set.index != IP_SET_INVALID_ID)
+ ip_set_put(info->del_set.index);
}
static struct ipt_target SET_target = {
@@ -100,6 +105,10 @@
.me = THIS_MODULE
};
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("iptables IP set target module");
+
static int __init init(void)
{
return ipt_register_target(&SET_target);
@@ -112,4 +121,3 @@
module_init(init);
module_exit(fini);
-MODULE_LICENSE("GPL");
Modified: trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ipt_set.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ipt_set.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux/net/ipv4/netfilter/ipt_set.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -1,5 +1,15 @@
-/* Kernel module to match an IP address set. */
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Martin Josefsson <gandalf at wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+/* Kernel module to match an IP set. */
+
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
@@ -12,12 +22,8 @@
match_set(const struct ipt_set_info *info,
const struct sk_buff *skb,
int inv)
-{
- if (ip_set_testip_kernel(ip_set_list[info->id],
- skb,
- info->flags,
- info->set_level,
- info->ip_level))
+{
+ if (ip_set_testip_kernel(info->index, skb, info->flags))
inv = !inv;
return inv;
}
@@ -33,13 +39,10 @@
int *hotdrop)
{
const struct ipt_set_info_match *info = matchinfo;
-
- if (info->match.id < 0)
- return 0;
- return match_set(&info->match,
+ return match_set(&info->match_set,
skb,
- info->match.flags[0] & IPSET_MATCH_INV);
+ info->match_set.flags[0] & IPSET_MATCH_INV);
}
static int
@@ -49,22 +52,22 @@
unsigned int matchsize,
unsigned int hook_mask)
{
- struct ipt_set_info_match *info = matchinfo;
+ struct ipt_set_info_match *info =
+ (struct ipt_set_info_match *) matchinfo;
+ ip_set_id_t index;
if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) {
ip_set_printk("invalid matchsize %d", matchsize);
return 0;
}
- if (info->match.id < 0)
- return 1;
+ index = ip_set_get_byindex(info->match_set.index);
- if (!ip_set_get_byid(info->match.id)) {
- ip_set_printk("cannot verify setid %i to match",
- info->match.id);
+ if (index == IP_SET_INVALID_ID) {
+ ip_set_printk("Cannot find set indentified by id %u to match",
+ info->match_set.index);
return 0; /* error */
}
- DP("checkentry OK");
return 1;
}
@@ -78,8 +81,7 @@
return;
}
- if (info->match.id >= 0)
- ip_set_put(ip_set_list[info->match.id]);
+ ip_set_put(info->match_set.index);
}
static struct ipt_match set_match = {
@@ -90,6 +92,10 @@
.me = THIS_MODULE
};
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("iptables IP set match module");
+
static int __init init(void)
{
return ipt_register_match(&set_match);
@@ -102,4 +108,3 @@
module_init(init);
module_exit(fini);
-MODULE_LICENSE("GPL");
Added: trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,489 @@
+#ifndef _IP_SET_H
+#define _IP_SET_H
+
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Martin Josefsson <gandalf at wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * A sockopt of such quality has hardly ever been seen before on the open
+ * market! This little beauty, hardly ever used: above 64, so it's
+ * traditionally used for firewalling, not touched (even once!) by the
+ * 2.0, 2.2 and 2.4 kernels!
+ *
+ * Comes with its own certificate of authenticity, valid anywhere in the
+ * Free world!
+ *
+ * Rusty, 19.4.2000
+ */
+#define SO_IP_SET 83
+
+/*
+ * Heavily modify by Joakim Axelsson 08.03.2002
+ * - Made it more modulebased
+ *
+ * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004
+ * - bindings added
+ * - in order to "deal with" backward compatibility, renamed to ipset
+ */
+
+/*
+ * Used so that the kernel module and ipset-binary can match their versions
+ */
+#define IP_SET_PROTOCOL_VERSION 2
+
+#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */
+
+/* Lets work with our own typedef for representing an IP address.
+ * We hope to make the code more portable, possibly to IPv6...
+ *
+ * The representation works in HOST byte order, because most set types
+ * will perform arithmetic operations and compare operations.
+ *
+ * For now the type is an uint32_t.
+ *
+ * Make sure to ONLY use the functions when translating and parsing
+ * in order to keep the host byte order and make it more portable:
+ * parse_ip()
+ * parse_mask()
+ * parse_ipandmask()
+ * ip_tostring()
+ * (Joakim: where are they???)
+ */
+
+typedef uint32_t ip_set_ip_t;
+
+/* Sets are identified by an id in kernel space. Tweak with ip_set_id_t
+ * and IP_SET_INVALID_ID if you want to increase the max number of sets.
+ */
+typedef uint16_t ip_set_id_t;
+
+#define IP_SET_INVALID_ID 65535
+
+/* How deep we follow bindings (minus one) */
+#define IP_SET_MAX_BINDINGS 7
+
+/*
+ * Option flags for kernel operations (ipt_set_info)
+ */
+#define IPSET_SRC 0x01 /* Source match/add */
+#define IPSET_DST 0x02 /* Destination match/add */
+#define IPSET_MATCH_INV 0x04 /* Inverse matching */
+
+/*
+ * Set types (flavours)
+ */
+#define IPSET_TYPE_IP 0 /* IP address type of set */
+#define IPSET_TYPE_PORT 1 /* Port type of set */
+
+/* Reserved keywords */
+#define IPSET_TOKEN_DEFAULT ":default:"
+#define IPSET_TOKEN_ALL ":all:"
+
+/* SO_IP_SET operation constants, and their request struct types.
+ *
+ * Operation ids:
+ * 0-99: commands with version checking
+ * 100-199: add/del/test/bind/unbind
+ * 200-299: list, save, restore
+ */
+
+/* Single shot operations:
+ * version, create, destroy, flush, rename and swap
+ *
+ * Sets are identified by name.
+ */
+
+#define IP_SET_REQ_STD \
+ unsigned op; \
+ unsigned version; \
+ char name[IP_SET_MAXNAMELEN]
+
+#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */
+struct ip_set_req_create {
+ IP_SET_REQ_STD;
+ char typename[IP_SET_MAXNAMELEN];
+};
+
+#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */
+struct ip_set_req_std {
+ IP_SET_REQ_STD;
+};
+
+#define IP_SET_OP_FLUSH 0x00000003 /* Remove all IPs in a set */
+/* Uses ip_set_req_std */
+
+#define IP_SET_OP_RENAME 0x00000004 /* Rename a set */
+/* Uses ip_set_req_create */
+
+#define IP_SET_OP_SWAP 0x00000005 /* Swap two sets */
+/* Uses ip_set_req_create */
+
+union ip_set_name_index {
+ char name[IP_SET_MAXNAMELEN];
+ ip_set_id_t index;
+};
+
+#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */
+struct ip_set_req_get_set {
+ unsigned op;
+ unsigned version;
+ union ip_set_name_index set;
+};
+
+#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */
+/* Uses ip_set_req_get_set */
+
+#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
+struct ip_set_req_version {
+ unsigned op;
+ unsigned version;
+};
+
+/* Double shots operations:
+ * add, del, test, bind and unbind.
+ *
+ * First we query the kernel to get the index and type of the target set,
+ * then issue the command. Validity of IP is checked in kernel in order
+ * to minimalize sockopt operations.
+ */
+
+/* Get minimal set data for add/del/test/bind/unbind IP */
+#define IP_SET_OP_ADT_GET 0x00000010 /* Get set and type */
+struct ip_set_req_adt_get {
+ unsigned op;
+ unsigned version;
+ union ip_set_name_index set;
+ char typename[IP_SET_MAXNAMELEN];
+};
+
+#define IP_SET_REQ_BYINDEX \
+ unsigned op; \
+ ip_set_id_t index;
+
+struct ip_set_req_adt {
+ IP_SET_REQ_BYINDEX;
+};
+
+#define IP_SET_OP_ADD_IP 0x00000101 /* Add an IP to a set */
+/* Uses ip_set_req_adt, with type specific addage */
+
+#define IP_SET_OP_DEL_IP 0x00000102 /* Remove an IP from a set */
+/* Uses ip_set_req_adt, with type specific addage */
+
+#define IP_SET_OP_TEST_IP 0x00000103 /* Test an IP in a set */
+/* Uses ip_set_req_adt, with type specific addage */
+
+#define IP_SET_OP_BIND_SET 0x00000104 /* Bind an IP to a set */
+/* Uses ip_set_req_bind, with type specific addage */
+struct ip_set_req_bind {
+ IP_SET_REQ_BYINDEX;
+ char binding[IP_SET_MAXNAMELEN];
+};
+
+#define IP_SET_OP_UNBIND_SET 0x00000105 /* Unbind an IP from a set */
+/* Uses ip_set_req_bind, with type speficic addage
+ * index = 0 means unbinding for all sets */
+
+#define IP_SET_OP_TEST_BIND_SET 0x00000106 /* Test binding an IP to a set */
+/* Uses ip_set_req_bind, with type specific addage */
+
+/* Multiple shots operations: list, save, restore.
+ *
+ * - check kernel version and query the max number of sets
+ * - get the basic information on all sets
+ * and size required for the next step
+ * - get actual set data: header, data, bindings
+ */
+
+/* Get max_sets and the index of a queried set
+ */
+#define IP_SET_OP_MAX_SETS 0x00000020
+struct ip_set_req_max_sets {
+ unsigned op;
+ unsigned version;
+ ip_set_id_t max_sets; /* max_sets */
+ ip_set_id_t sets; /* real number of sets */
+ union ip_set_name_index set; /* index of set if name used */
+};
+
+/* Get the id and name of the sets plus size for next step */
+#define IP_SET_OP_LIST_SIZE 0x00000201
+#define IP_SET_OP_SAVE_SIZE 0x00000202
+struct ip_set_req_setnames {
+ unsigned op;
+ ip_set_id_t index; /* set to list/save */
+ size_t size; /* size to get setdata/bindings */
+ /* followed by sets number of struct ip_set_name_list */
+};
+
+struct ip_set_name_list {
+ char name[IP_SET_MAXNAMELEN];
+ char typename[IP_SET_MAXNAMELEN];
+ ip_set_id_t index;
+ ip_set_id_t id;
+};
+
+/* The actual list operation */
+#define IP_SET_OP_LIST 0x00000203
+struct ip_set_req_list {
+ IP_SET_REQ_BYINDEX;
+ /* sets number of struct ip_set_list in reply */
+};
+
+struct ip_set_list {
+ ip_set_id_t index;
+ ip_set_id_t binding;
+ u_int32_t ref;
+ size_t header_size; /* Set header data of header_size */
+ size_t members_size; /* Set members data of members_size */
+ size_t bindings_size; /* Set bindings data of bindings_size */
+};
+
+struct ip_set_hash_list {
+ ip_set_ip_t ip;
+ ip_set_id_t binding;
+};
+
+/* The save operation */
+#define IP_SET_OP_SAVE 0x00000204
+/* Uses ip_set_req_list, in the reply replaced by
+ * sets number of struct ip_set_save plus a marker
+ * ip_set_save followed by ip_set_hash_save structures.
+ */
+struct ip_set_save {
+ ip_set_id_t index;
+ ip_set_id_t binding;
+ size_t header_size; /* Set header data of header_size */
+ size_t members_size; /* Set members data of members_size */
+};
+
+/* At restoring, ip == 0 means default binding for the given set: */
+struct ip_set_hash_save {
+ ip_set_ip_t ip;
+ ip_set_id_t id;
+ ip_set_id_t binding;
+};
+
+/* The restore operation */
+#define IP_SET_OP_RESTORE 0x00000205
+/* Uses ip_set_req_setnames followed by ip_set_restore structures
+ * plus a marker ip_set_restore, followed by ip_set_hash_save
+ * structures.
+ */
+struct ip_set_restore {
+ char name[IP_SET_MAXNAMELEN];
+ char typename[IP_SET_MAXNAMELEN];
+ ip_set_id_t index;
+ size_t header_size; /* Create data of header_size */
+ size_t members_size; /* Set members data of members_size */
+};
+
+static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b)
+{
+ return 4 * ((((b - a + 8) / 8) + 3) / 4);
+}
+
+#ifdef __KERNEL__
+
+#define ip_set_printk(format, args...) \
+ do { \
+ printk("%s: %s: ", __FILE__, __FUNCTION__); \
+ printk(format "\n" , ## args); \
+ } while (0)
+
+#if defined(IP_SET_DEBUG)
+#define DP(format, args...) \
+ do { \
+ printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\
+ printk(format "\n" , ## args); \
+ } while (0)
+#define IP_SET_ASSERT(x) \
+ do { \
+ if (!(x)) \
+ printk("IP_SET_ASSERT: %s:%i(%s)\n", \
+ __FILE__, __LINE__, __FUNCTION__); \
+ } while (0)
+#else
+#define DP(format, args...)
+#define IP_SET_ASSERT(x)
+#endif
+
+struct ip_set;
+
+/*
+ * The ip_set_type definition - one per set type, e.g. "ipmap".
+ *
+ * Each individual set has a pointer, set->type, going to one
+ * of these structures. Function pointers inside the structure implement
+ * the real behaviour of the sets.
+ *
+ * If not mentioned differently, the implementation behind the function
+ * pointers of a set_type, is expected to return 0 if ok, and a negative
+ * errno (e.g. -EINVAL) on error.
+ */
+struct ip_set_type {
+ struct list_head list; /* next in list of set types */
+
+ /* test for IP in set (kernel: iptables -m set src|dst)
+ * return 0 if not in set, 1 if in set.
+ */
+ int (*testip_kernel) (struct ip_set *set,
+ const struct sk_buff * skb,
+ u_int32_t flags,
+ ip_set_ip_t *ip);
+
+ /* test for IP in set (userspace: ipset -T set IP)
+ * return 0 if not in set, 1 if in set.
+ */
+ int (*testip) (struct ip_set *set,
+ const void *data, size_t size,
+ ip_set_ip_t *ip);
+
+ /*
+ * Size of the data structure passed by when
+ * adding/deletin/testing an entry.
+ */
+ size_t reqsize;
+
+ /* Add IP into set (userspace: ipset -A set IP)
+ * Return -EEXIST if the address is already in the set,
+ * and -ERANGE if the address lies outside the set bounds.
+ * If the address was not already in the set, 0 is returned.
+ */
+ int (*addip) (struct ip_set *set,
+ const void *data, size_t size,
+ ip_set_ip_t *ip);
+
+ /* Add IP into set (kernel: iptables ... -j SET set src|dst)
+ * Return -EEXIST if the address is already in the set,
+ * and -ERANGE if the address lies outside the set bounds.
+ * If the address was not already in the set, 0 is returned.
+ */
+ int (*addip_kernel) (struct ip_set *set,
+ const struct sk_buff * skb,
+ u_int32_t flags,
+ ip_set_ip_t *ip);
+
+ /* remove IP from set (userspace: ipset -D set --entry x)
+ * Return -EEXIST if the address is NOT in the set,
+ * and -ERANGE if the address lies outside the set bounds.
+ * If the address really was in the set, 0 is returned.
+ */
+ int (*delip) (struct ip_set *set,
+ const void *data, size_t size,
+ ip_set_ip_t *ip);
+
+ /* remove IP from set (kernel: iptables ... -j SET --entry x)
+ * Return -EEXIST if the address is NOT in the set,
+ * and -ERANGE if the address lies outside the set bounds.
+ * If the address really was in the set, 0 is returned.
+ */
+ int (*delip_kernel) (struct ip_set *set,
+ const struct sk_buff * skb,
+ u_int32_t flags,
+ ip_set_ip_t *ip);
+
+ /* new set creation - allocated type specific items
+ */
+ int (*create) (struct ip_set *set,
+ const void *data, size_t size);
+
+ /* retry the operation after successfully tweaking the set
+ */
+ int (*retry) (struct ip_set *set);
+
+ /* set destruction - free type specific items
+ * There is no return value.
+ * Can be called only when child sets are destroyed.
+ */
+ void (*destroy) (struct ip_set *set);
+
+ /* set flushing - reset all bits in the set, or something similar.
+ * There is no return value.
+ */
+ void (*flush) (struct ip_set *set);
+
+ /* Listing: Get size needed for header
+ */
+ int (*list_header_size) (const struct ip_set *set);
+
+ /* Listing: Get the header
+ *
+ * Fill in the information in "data".
+ * This function is always run after list_header_size() under a
+ * writelock on the set. Therefor is the length of "data" always
+ * correct.
+ */
+ void (*list_header) (const struct ip_set *set,
+ void *data);
+
+ /* Listing: Get the size for the set members
+ */
+ int (*list_members_size) (const struct ip_set *set);
+
+ /* Listing: Get the set members
+ *
+ * Fill in the information in "data".
+ * This function is always run after list_member_size() under a
+ * writelock on the set. Therefor is the length of "data" always
+ * correct.
+ */
+ void (*list_members) (const struct ip_set *set,
+ void *data);
+
+ char typename[IP_SET_MAXNAMELEN];
+ char typecode;
+ int protocol_version;
+
+ /* Set this to THIS_MODULE if you are a module, otherwise NULL */
+ struct module *me;
+};
+
+extern int ip_set_register_set_type(struct ip_set_type *set_type);
+extern void ip_set_unregister_set_type(struct ip_set_type *set_type);
+
+/* A generic ipset */
+struct ip_set {
+ char name[IP_SET_MAXNAMELEN]; /* the name of the set */
+ rwlock_t lock; /* lock for concurrency control */
+ ip_set_id_t id; /* set id for swapping */
+ ip_set_id_t binding; /* default binding for the set */
+ atomic_t ref; /* in kernel and in hash references */
+ struct ip_set_type *type; /* the set types */
+ void *data; /* pooltype specific data */
+};
+
+/* Structure to bind set elements to sets */
+struct ip_set_hash {
+ struct list_head list; /* list of clashing entries in hash */
+ ip_set_ip_t ip; /* ip from set */
+ ip_set_id_t id; /* set id */
+ ip_set_id_t binding; /* set we bind the element to */
+};
+
+/* register and unregister set references */
+extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]);
+extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id);
+extern void ip_set_put(ip_set_id_t id);
+
+/* API for iptables set match, and SET target */
+extern void ip_set_addip_kernel(ip_set_id_t id,
+ const struct sk_buff *skb,
+ const u_int32_t *flags);
+extern void ip_set_delip_kernel(ip_set_id_t id,
+ const struct sk_buff *skb,
+ const u_int32_t *flags);
+extern int ip_set_testip_kernel(ip_set_id_t id,
+ const struct sk_buff *skb,
+ const u_int32_t *flags);
+
+#endif /* __KERNEL__ */
+
+#endif /*_IP_SET_H*/
Added: trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_iphash.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_iphash.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_iphash.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,30 @@
+#ifndef __IP_SET_IPHASH_H
+#define __IP_SET_IPHASH_H
+
+#include <linux/netfilter_ipv4/ip_set.h>
+
+#define SETTYPE_NAME "iphash"
+#define MAX_RANGE 0x0000FFFF
+
+struct ip_set_iphash {
+ ip_set_ip_t *members; /* the iphash proper */
+ uint32_t initval; /* initval for jhash_1word */
+ uint32_t prime; /* prime for double hashing */
+ uint32_t hashsize; /* hash size */
+ uint16_t probes; /* max number of probes */
+ uint16_t resize; /* resize factor in percent */
+ ip_set_ip_t netmask; /* netmask */
+};
+
+struct ip_set_req_iphash_create {
+ uint32_t hashsize;
+ uint16_t probes;
+ uint16_t resize;
+ ip_set_ip_t netmask;
+};
+
+struct ip_set_req_iphash {
+ ip_set_ip_t ip;
+};
+
+#endif /* __IP_SET_IPHASH_H */
Added: trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_ipmap.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_ipmap.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_ipmap.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,56 @@
+#ifndef __IP_SET_IPMAP_H
+#define __IP_SET_IPMAP_H
+
+#include <linux/netfilter_ipv4/ip_set.h>
+
+#define SETTYPE_NAME "ipmap"
+#define MAX_RANGE 0x0000FFFF
+
+struct ip_set_ipmap {
+ void *members; /* the ipmap proper */
+ ip_set_ip_t first_ip; /* host byte order, included in range */
+ ip_set_ip_t last_ip; /* host byte order, included in range */
+ ip_set_ip_t netmask; /* subnet netmask */
+ ip_set_ip_t sizeid; /* size of set in IPs */
+ u_int16_t hosts; /* number of hosts in a subnet */
+};
+
+struct ip_set_req_ipmap_create {
+ ip_set_ip_t from;
+ ip_set_ip_t to;
+ ip_set_ip_t netmask;
+};
+
+struct ip_set_req_ipmap {
+ ip_set_ip_t ip;
+};
+
+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;
+}
+
+ip_set_ip_t
+range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits)
+{
+ ip_set_ip_t mask = 0xFFFFFFFE;
+
+ *bits = 32;
+ while (--(*bits) >= 0 && mask && (to & mask) != from)
+ mask <<= 1;
+
+ return mask;
+}
+
+#endif /* __IP_SET_IPMAP_H */
Added: trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_jhash.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_jhash.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_jhash.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,148 @@
+#ifndef _LINUX_IPSET_JHASH_H
+#define _LINUX_IPSET_JHASH_H
+
+/* This is a copy of linux/jhash.h but the types u32/u8 are changed
+ * to __u32/__u8 so that the header file can be included into
+ * userspace code as well. Jozsef Kadlecsik (kadlec at blackhole.kfki.hu)
+ */
+
+/* jhash.h: Jenkins hash support.
+ *
+ * Copyright (C) 1996 Bob Jenkins (bob_jenkins at burtleburtle.net)
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
+ * hash(), hash2(), hash3, and mix() are externally useful functions.
+ * Routines to test the hash are included if SELF_TEST is defined.
+ * You can use this free for any purpose. It has no warranty.
+ *
+ * Copyright (C) 2003 David S. Miller (davem at redhat.com)
+ *
+ * I've modified Bob's hash to be useful in the Linux kernel, and
+ * any bugs present are surely my fault. -DaveM
+ */
+
+/* NOTE: Arguments are modified. */
+#define __jhash_mix(a, b, c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+/* The golden ration: an arbitrary value */
+#define JHASH_GOLDEN_RATIO 0x9e3779b9
+
+/* The most generic version, hashes an arbitrary sequence
+ * of bytes. No alignment or length assumptions are made about
+ * the input key.
+ */
+static inline __u32 jhash(void *key, __u32 length, __u32 initval)
+{
+ __u32 a, b, c, len;
+ __u8 *k = key;
+
+ len = length;
+ a = b = JHASH_GOLDEN_RATIO;
+ c = initval;
+
+ while (len >= 12) {
+ a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24));
+ b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24));
+ c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24));
+
+ __jhash_mix(a,b,c);
+
+ k += 12;
+ len -= 12;
+ }
+
+ c += length;
+ switch (len) {
+ case 11: c += ((__u32)k[10]<<24);
+ case 10: c += ((__u32)k[9]<<16);
+ case 9 : c += ((__u32)k[8]<<8);
+ case 8 : b += ((__u32)k[7]<<24);
+ case 7 : b += ((__u32)k[6]<<16);
+ case 6 : b += ((__u32)k[5]<<8);
+ case 5 : b += k[4];
+ case 4 : a += ((__u32)k[3]<<24);
+ case 3 : a += ((__u32)k[2]<<16);
+ case 2 : a += ((__u32)k[1]<<8);
+ case 1 : a += k[0];
+ };
+
+ __jhash_mix(a,b,c);
+
+ return c;
+}
+
+/* A special optimized version that handles 1 or more of __u32s.
+ * The length parameter here is the number of __u32s in the key.
+ */
+static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval)
+{
+ __u32 a, b, c, len;
+
+ a = b = JHASH_GOLDEN_RATIO;
+ c = initval;
+ len = length;
+
+ while (len >= 3) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ __jhash_mix(a, b, c);
+ k += 3; len -= 3;
+ }
+
+ c += length * 4;
+
+ switch (len) {
+ case 2 : b += k[1];
+ case 1 : a += k[0];
+ };
+
+ __jhash_mix(a,b,c);
+
+ return c;
+}
+
+
+/* A special ultra-optimized versions that knows they are hashing exactly
+ * 3, 2 or 1 word(s).
+ *
+ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
+ * done at the end is not done here.
+ */
+static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval)
+{
+ a += JHASH_GOLDEN_RATIO;
+ b += JHASH_GOLDEN_RATIO;
+ c += initval;
+
+ __jhash_mix(a, b, c);
+
+ return c;
+}
+
+static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval)
+{
+ return jhash_3words(a, b, 0, initval);
+}
+
+static inline __u32 jhash_1word(__u32 a, __u32 initval)
+{
+ return jhash_3words(a, 0, 0, initval);
+}
+
+#endif /* _LINUX_IPSET_JHASH_H */
Added: trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_macipmap.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_macipmap.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_macipmap.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,38 @@
+#ifndef __IP_SET_MACIPMAP_H
+#define __IP_SET_MACIPMAP_H
+
+#include <linux/netfilter_ipv4/ip_set.h>
+
+#define SETTYPE_NAME "macipmap"
+#define MAX_RANGE 0x0000FFFF
+
+/* general flags */
+#define IPSET_MACIP_MATCHUNSET 1
+
+/* per ip flags */
+#define IPSET_MACIP_ISSET 1
+
+struct ip_set_macipmap {
+ void *members; /* the macipmap proper */
+ ip_set_ip_t first_ip; /* host byte order, included in range */
+ ip_set_ip_t last_ip; /* host byte order, included in range */
+ u_int32_t flags;
+};
+
+struct ip_set_req_macipmap_create {
+ ip_set_ip_t from;
+ ip_set_ip_t to;
+ u_int32_t flags;
+};
+
+struct ip_set_req_macipmap {
+ ip_set_ip_t ip;
+ unsigned char ethernet[ETH_ALEN];
+};
+
+struct ip_set_macip {
+ unsigned short flags;
+ unsigned char ethernet[ETH_ALEN];
+};
+
+#endif /* __IP_SET_MACIPMAP_H */
Added: trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_portmap.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_portmap.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_portmap.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,25 @@
+#ifndef __IP_SET_PORTMAP_H
+#define __IP_SET_PORTMAP_H
+
+#include <linux/netfilter_ipv4/ip_set.h>
+
+#define SETTYPE_NAME "portmap"
+#define MAX_RANGE 0x0000FFFF
+#define INVALID_PORT (MAX_RANGE + 1)
+
+struct ip_set_portmap {
+ void *members; /* the portmap proper */
+ ip_set_ip_t first_port; /* host byte order, included in range */
+ ip_set_ip_t last_port; /* host byte order, included in range */
+};
+
+struct ip_set_req_portmap_create {
+ ip_set_ip_t from;
+ ip_set_ip_t to;
+};
+
+struct ip_set_req_portmap {
+ ip_set_ip_t port;
+};
+
+#endif /* __IP_SET_PORTMAP_H */
Added: trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_prime.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_prime.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ip_set_prime.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,34 @@
+#ifndef __IP_SET_PRIME_H
+#define __IP_SET_PRIME_H
+
+static inline unsigned make_prime_bound(unsigned nr)
+{
+ unsigned long long nr64 = nr;
+ unsigned long long x = 1;
+ nr = 1;
+ while (x <= nr64) { x <<= 2; nr <<= 1; }
+ return nr;
+}
+
+static inline int make_prime_check(unsigned nr)
+{
+ unsigned x = 3;
+ unsigned b = make_prime_bound(nr);
+ while (x <= b) {
+ if (0 == (nr % x)) return 0;
+ x += 2;
+ }
+ return 1;
+}
+
+static unsigned make_prime(unsigned nr)
+{
+ if (0 == (nr & 1)) nr--;
+ while (nr > 1) {
+ if (make_prime_check(nr)) return nr;
+ nr -= 2;
+ }
+ return 2;
+}
+
+#endif /* __IP_SET_PRIME_H */
Added: trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ipt_set.h
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ipt_set.h 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/include/linux/netfilter_ipv4/ipt_set.h 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,21 @@
+#ifndef _IPT_SET_H
+#define _IPT_SET_H
+
+#include <linux/netfilter_ipv4/ip_set.h>
+
+struct ipt_set_info {
+ ip_set_id_t index;
+ u_int32_t flags[IP_SET_MAX_BINDINGS];
+};
+
+/* match info */
+struct ipt_set_info_match {
+ struct ipt_set_info match_set;
+};
+
+struct ipt_set_info_target {
+ struct ipt_set_info add_set;
+ struct ipt_set_info del_set;
+};
+
+#endif /*_IPT_SET_H*/
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Kconfig.ladd
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Kconfig.ladd 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Kconfig.ladd 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,84 @@
+config IP_NF_SET
+ tristate "IP set support"
+ depends on INET && NETFILTER
+ help
+ This option adds IP set support to the kernel.
+ In order to define and use sets, you need the userspace utility
+ ipset(8).
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config IP_NF_SET_MAX
+ int "Maximum number of IP sets"
+ default 256
+ range 2 65534
+ depends on IP_NF_SET
+ help
+ You can define here default value of the maximum number
+ of IP sets for the kernel.
+
+ The value can be overriden by the 'max_sets' module
+ parameter of the 'ip_set' module.
+
+config IP_NF_SET_HASHSIZE
+ int "Hash size for bindings of IP sets"
+ default 1024
+ depends on IP_NF_SET
+ help
+ You can define here default value of the hash size for
+ bindings of IP sets.
+
+ The value can be overriden by the 'hash_size' module
+ parameter of the 'ip_set' module.
+
+config IP_NF_SET_IPMAP
+ tristate "ipmap set support"
+ depends on IP_NF_SET
+ help
+ This option adds the ipmap set type support.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config IP_NF_SET_MACIPMAP
+ tristate "macipmap set support"
+ depends on IP_NF_SET
+ help
+ This option adds the macipmap set type support.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config IP_NF_SET_PORTMAP
+ tristate "portmap set support"
+ depends on IP_NF_SET
+ help
+ This option adds the portmap set type support.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config IP_NF_SET_IPHASH
+ tristate "iphash set support"
+ depends on IP_NF_SET
+ help
+ This option adds the iphash set type support.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config IP_NF_MATCH_SET
+ tristate "set match support"
+ depends on IP_NF_SET
+ help
+ Set matching matches against given IP sets.
+ You need the ipset utility to create and set up the sets.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config IP_NF_TARGET_SET
+ tristate "SET target support"
+ depends on IP_NF_SET
+ help
+ The SET target makes possible to add/delete entries
+ in IP sets.
+ You need the ipset utility to create and set up the sets.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Makefile.ladd
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Makefile.ladd 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Makefile.ladd 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,2 @@
+obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.o
+obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Makefile.ladd_2
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Makefile.ladd_2 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/Makefile.ladd_2 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,9 @@
+obj-$(CONFIG_IP_NF_TARGET_NOTRACK) += ipt_NOTRACK.o
+obj-$(CONFIG_IP_NF_TARGET_SET) += ipt_SET.o
+
+# sets
+obj-$(CONFIG_IP_NF_SET) += ip_set.o
+obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o
+obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o
+obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o
+obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.o
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,1986 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Kernel module for IP set management */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/random.h>
+#include <linux/jhash.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+
+#define ASSERT_READ_LOCK(x) /* dont use that */
+#define ASSERT_WRITE_LOCK(x)
+#include <linux/netfilter_ipv4/listhelp.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+
+static struct list_head set_type_list; /* all registered sets */
+static struct ip_set **ip_set_list; /* all individual sets */
+static rwlock_t ip_set_lock = RW_LOCK_UNLOCKED; /* protects the lists and the hash */
+static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */
+static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX;
+static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE;
+static struct list_head *ip_set_hash; /* hash of bindings */
+static unsigned int ip_set_hash_random; /* random seed */
+
+/*
+ * Sets are identified either by the index in ip_set_list or by id.
+ * The id never changes and is used to find a key in the hash.
+ * The index may change by swapping and used at all other places
+ * (set/SET netfilter modules, binding value, etc.)
+ *
+ * Userspace requests are serialized by ip_set_mutex and sets can
+ * be deleted only from userspace. Therefore ip_set_list locking
+ * must obey the following rules:
+ *
+ * - kernel requests: read and write locking mandatory
+ * - user requests: read locking optional, write locking mandatory
+ */
+
+static inline void
+__ip_set_get(ip_set_id_t index)
+{
+ atomic_inc(&ip_set_list[index]->ref);
+}
+
+static inline void
+__ip_set_put(ip_set_id_t index)
+{
+ atomic_dec(&ip_set_list[index]->ref);
+}
+
+/*
+ * Binding routines
+ */
+
+static inline int
+ip_hash_cmp(const struct ip_set_hash *set_hash,
+ ip_set_id_t id, ip_set_ip_t ip)
+{
+ return set_hash->id == id && set_hash->ip == ip;
+}
+
+static ip_set_id_t
+ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip)
+{
+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random)
+ % ip_set_bindings_hash_size;
+ struct ip_set_hash *set_hash;
+
+ MUST_BE_READ_LOCKED(&ip_set_lock);
+ IP_SET_ASSERT(ip_set_list[id]);
+ DP("set: %s, ip: %u", ip_set_list[id]->name, ip);
+
+ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
+ struct ip_set_hash *, id, ip);
+
+ DP("set: %s, ip: %u, binding: %s", ip_set_list[id]->name, ip,
+ set_hash != NULL ? ip_set_list[set_hash->binding]->name : "");
+
+ return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID);
+}
+
+static inline void
+__set_hash_del(struct ip_set_hash *set_hash)
+{
+ MUST_BE_WRITE_LOCKED(&ip_set_lock);
+ IP_SET_ASSERT(ip_set_list[set_hash->binding]);
+
+ __ip_set_put(set_hash->binding);
+ list_del(&set_hash->list);
+ kfree(set_hash);
+}
+
+static int
+ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip)
+{
+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random)
+ % ip_set_bindings_hash_size;
+ struct ip_set_hash *set_hash;
+
+ IP_SET_ASSERT(ip_set_list[id]);
+ DP("set: %s, ip: %u", ip_set_list[id]->name, ip);
+ write_lock_bh(&ip_set_lock);
+ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
+ struct ip_set_hash *, id, ip);
+ DP("set: %s, ip: %u, binding: %s", ip_set_list[id]->name, ip,
+ set_hash != NULL ? ip_set_list[set_hash->binding]->name : "");
+
+ if (set_hash != NULL)
+ __set_hash_del(set_hash);
+ write_unlock_bh(&ip_set_lock);
+ return 0;
+}
+
+static int
+ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding)
+{
+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random)
+ % ip_set_bindings_hash_size;
+ struct ip_set_hash *set_hash;
+ int ret = 0;
+
+ IP_SET_ASSERT(ip_set_list[id]);
+ IP_SET_ASSERT(ip_set_list[binding]);
+ DP("set: %s, ip: %u, binding: %s", ip_set_list[id]->name,
+ ip, ip_set_list[binding]->name);
+ write_lock_bh(&ip_set_lock);
+ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
+ struct ip_set_hash *, id, ip);
+ if (!set_hash) {
+ set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_KERNEL);
+ if (!set_hash) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+ INIT_LIST_HEAD(&set_hash->list);
+ set_hash->id = id;
+ set_hash->ip = ip;
+ list_add(&ip_set_hash[key], &set_hash->list);
+ } else {
+ IP_SET_ASSERT(ip_set_list[set_hash->binding]);
+ DP("overwrite binding: %s",
+ ip_set_list[set_hash->binding]->name);
+ __ip_set_put(set_hash->binding);
+ }
+ set_hash->binding = binding;
+ __ip_set_get(set_hash->binding);
+ unlock:
+ write_unlock_bh(&ip_set_lock);
+ return ret;
+}
+
+#define FOREACH_HASH_DO(fn, args...) \
+({ \
+ ip_set_id_t __key; \
+ struct ip_set_hash *__set_hash; \
+ \
+ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \
+ list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \
+ fn(__set_hash , ## args); \
+ } \
+})
+
+#define FOREACH_HASH_RW_DO(fn, args...) \
+({ \
+ ip_set_id_t __key; \
+ struct ip_set_hash *__set_hash, *__n; \
+ \
+ MUST_BE_WRITE_LOCKED(&ip_set_lock); \
+ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \
+ list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\
+ fn(__set_hash , ## args); \
+ } \
+})
+
+/* Add, del and test set entries from kernel */
+
+#define follow_bindings(index, set, ip) \
+((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \
+ || (index = (set)->binding) != IP_SET_INVALID_ID)
+
+int
+ip_set_testip_kernel(ip_set_id_t index,
+ const struct sk_buff *skb,
+ const u_int32_t *flags)
+{
+ struct ip_set *set;
+ ip_set_ip_t ip;
+ int res, i = 0;
+
+ IP_SET_ASSERT(flags[i]);
+ read_lock_bh(&ip_set_lock);
+ do {
+ set = ip_set_list[index];
+ IP_SET_ASSERT(set);
+ DP("set %s, index %u", set->name, index);
+ read_lock_bh(&set->lock);
+ res = set->type->testip_kernel(set, skb, flags[i], &ip);
+ read_unlock_bh(&set->lock);
+ } while (res > 0
+ && flags[++i]
+ && follow_bindings(index, set, ip));
+ read_unlock_bh(&ip_set_lock);
+
+ return res;
+}
+
+void
+ip_set_addip_kernel(ip_set_id_t index,
+ const struct sk_buff *skb,
+ const u_int32_t *flags)
+{
+ struct ip_set *set;
+ ip_set_ip_t ip;
+ int res, i= 0;
+
+ IP_SET_ASSERT(flags[i]);
+ read_lock_bh(&ip_set_lock);
+ do {
+ set = ip_set_list[index];
+ IP_SET_ASSERT(set);
+ DP("set %s, index %u", set->name, index);
+ write_lock_bh(&set->lock);
+ res = set->type->addip_kernel(set, skb, flags[i], &ip);
+ write_unlock_bh(&set->lock);
+ } while ((res == -EAGAIN
+ && set->type->retry
+ && (res = set->type->retry(set)) == 0)
+ || ((res == 0 || res == -EEXIST)
+ && flags[++i]
+ && follow_bindings(index, set, ip)));
+ read_unlock_bh(&ip_set_lock);
+}
+
+void
+ip_set_delip_kernel(ip_set_id_t index,
+ const struct sk_buff *skb,
+ const u_int32_t *flags)
+{
+ struct ip_set *set;
+ ip_set_ip_t ip;
+ int res, i = 0;
+
+ IP_SET_ASSERT(flags[i]);
+ read_lock_bh(&ip_set_lock);
+ do {
+ set = ip_set_list[index];
+ IP_SET_ASSERT(set);
+ DP("set %s, index %u", set->name, index);
+ write_lock_bh(&set->lock);
+ res = set->type->delip_kernel(set, skb, flags[i], &ip);
+ write_unlock_bh(&set->lock);
+ } while ((res == 0 || res == -EEXIST)
+ && flags[++i]
+ && follow_bindings(index, set, ip));
+ read_unlock_bh(&ip_set_lock);
+}
+
+/* Register and deregister settype */
+
+static inline int
+set_type_equal(const struct ip_set_type *set_type, const char *str2)
+{
+ return !strncmp(set_type->typename, str2, IP_SET_MAXNAMELEN - 1);
+}
+
+static inline struct ip_set_type *
+find_set_type(const char *name)
+{
+ return LIST_FIND(&set_type_list,
+ set_type_equal,
+ struct ip_set_type *,
+ name);
+}
+
+int
+ip_set_register_set_type(struct ip_set_type *set_type)
+{
+ int ret = 0;
+
+ if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) {
+ ip_set_printk("'%s' uses wrong protocol version %u (want %u)",
+ set_type->typename,
+ set_type->protocol_version,
+ IP_SET_PROTOCOL_VERSION);
+ return -EINVAL;
+ }
+
+ write_lock_bh(&ip_set_lock);
+ if (find_set_type(set_type->typename)) {
+ /* Duplicate! */
+ ip_set_printk("'%s' already registered!",
+ set_type->typename);
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (!try_module_get(THIS_MODULE)) {
+ ret = -EFAULT;
+ goto unlock;
+ }
+ list_append(&set_type_list, set_type);
+ DP("'%s' registered.", set_type->typename);
+ unlock:
+ write_unlock_bh(&ip_set_lock);
+ return ret;
+}
+
+void
+ip_set_unregister_set_type(struct ip_set_type *set_type)
+{
+ write_lock_bh(&ip_set_lock);
+ if (!find_set_type(set_type->typename)) {
+ ip_set_printk("'%s' not registered?",
+ set_type->typename);
+ goto unlock;
+ }
+ LIST_DELETE(&set_type_list, set_type);
+ module_put(THIS_MODULE);
+ DP("'%s' unregistered.", set_type->typename);
+ unlock:
+ write_unlock_bh(&ip_set_lock);
+
+}
+
+/*
+ * Userspace routines
+ */
+
+/*
+ * Find set by name, reference it once. The reference makes sure the
+ * thing pointed to, does not go away under our feet. Drop the reference
+ * later, using ip_set_put().
+ */
+ip_set_id_t
+ip_set_get_byname(const char *name)
+{
+ ip_set_id_t i, index = IP_SET_INVALID_ID;
+
+ down(&ip_set_app_mutex);
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && strcmp(ip_set_list[i]->name, name) == 0) {
+ __ip_set_get(i);
+ index = i;
+ break;
+ }
+ }
+ up(&ip_set_app_mutex);
+ return index;
+}
+
+/*
+ * Find set by index, reference it once. The reference makes sure the
+ * thing pointed to, does not go away under our feet. Drop the reference
+ * later, using ip_set_put().
+ */
+ip_set_id_t
+ip_set_get_byindex(ip_set_id_t index)
+{
+ down(&ip_set_app_mutex);
+
+ if (index >= ip_set_max)
+ return IP_SET_INVALID_ID;
+
+ if (ip_set_list[index])
+ __ip_set_get(index);
+ else
+ index = IP_SET_INVALID_ID;
+
+ up(&ip_set_app_mutex);
+ return index;
+}
+
+/*
+ * If the given set pointer points to a valid set, decrement
+ * reference count by 1. The caller shall not assume the index
+ * to be valid, after calling this function.
+ */
+void ip_set_put(ip_set_id_t index)
+{
+ down(&ip_set_app_mutex);
+ if (ip_set_list[index])
+ __ip_set_put(index);
+ up(&ip_set_app_mutex);
+}
+
+/* Find a set by name or index */
+static ip_set_id_t
+ip_set_find_byname(const char *name)
+{
+ ip_set_id_t i, index = IP_SET_INVALID_ID;
+
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && strcmp(ip_set_list[i]->name, name) == 0) {
+ index = i;
+ break;
+ }
+ }
+ return index;
+}
+
+static ip_set_id_t
+ip_set_find_byindex(ip_set_id_t index)
+{
+ if (index >= ip_set_max || ip_set_list[index] == NULL)
+ index = IP_SET_INVALID_ID;
+
+ return index;
+}
+
+/*
+ * Add, del, test, bind and unbind
+ */
+
+static inline int
+__ip_set_testip(struct ip_set *set,
+ const void *data,
+ size_t size,
+ ip_set_ip_t *ip)
+{
+ int res;
+
+ read_lock_bh(&set->lock);
+ res = set->type->testip(set, data, size, ip);
+ read_unlock_bh(&set->lock);
+
+ return res;
+}
+
+static int
+__ip_set_addip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
+ do {
+ write_lock_bh(&set->lock);
+ res = set->type->addip(set, data, size, &ip);
+ write_unlock_bh(&set->lock);
+ } while (res == -EAGAIN
+ && set->type->retry
+ && (res = set->type->retry(set)) == 0);
+
+ return res;
+}
+
+static int
+ip_set_addip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+
+ return __ip_set_addip(index,
+ data + sizeof(struct ip_set_req_adt),
+ size - sizeof(struct ip_set_req_adt));
+}
+
+static int
+ip_set_delip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
+ write_lock_bh(&set->lock);
+ res = set->type->delip(set,
+ data + sizeof(struct ip_set_req_adt),
+ size - sizeof(struct ip_set_req_adt),
+ &ip);
+ write_unlock_bh(&set->lock);
+
+ return res;
+}
+
+static int
+ip_set_testip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
+ res = __ip_set_testip(set,
+ data + sizeof(struct ip_set_req_adt),
+ size - sizeof(struct ip_set_req_adt),
+ &ip);
+
+ return (res > 0 ? -EEXIST : res);
+}
+
+static int
+ip_set_bindip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ struct ip_set_req_bind *req_bind;
+ ip_set_id_t binding;
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
+ if (size < sizeof(struct ip_set_req_bind))
+ return -EINVAL;
+
+ req_bind = (struct ip_set_req_bind *) data;
+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) {
+ /* Default binding of a set */
+ char *binding_name;
+
+ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN)
+ return -EINVAL;
+
+ binding_name = (char *)(data + sizeof(struct ip_set_req_bind));
+ binding_name[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ binding = ip_set_find_byname(binding_name);
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+ write_lock_bh(&ip_set_lock);
+ /* Sets as binding values are referenced */
+ if (set->binding != IP_SET_INVALID_ID)
+ __ip_set_put(set->binding);
+ set->binding = binding;
+ __ip_set_get(set->binding);
+ write_unlock_bh(&ip_set_lock);
+
+ return 0;
+ }
+ binding = ip_set_find_byname(req_bind->binding);
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+ res = __ip_set_testip(set,
+ data + sizeof(struct ip_set_req_bind),
+ size - sizeof(struct ip_set_req_bind),
+ &ip);
+ DP("set %s, ip: %u, binding %s",
+ set->name, ip, ip_set_list[binding]->name);
+
+ if (res >= 0)
+ res = ip_set_hash_add(set->id, ip, binding);
+
+ return res;
+}
+
+#define FOREACH_SET_DO(fn, args...) \
+({ \
+ ip_set_id_t __i; \
+ struct ip_set *__set; \
+ \
+ for (__i = 0; __i < ip_set_max; __i++) { \
+ __set = ip_set_list[__i]; \
+ if (__set != NULL) \
+ fn(__set , ##args); \
+ } \
+})
+
+static inline void
+__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id)
+{
+ if (set_hash->id == id)
+ __set_hash_del(set_hash);
+}
+
+static inline void
+__unbind_default(struct ip_set *set)
+{
+ if (set->binding != IP_SET_INVALID_ID) {
+ /* Sets as binding values are referenced */
+ __ip_set_put(set->binding);
+ set->binding = IP_SET_INVALID_ID;
+ }
+}
+
+static int
+ip_set_unbindip(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set;
+ struct ip_set_req_bind *req_bind;
+ ip_set_ip_t ip;
+ int res;
+
+ DP("");
+ if (size < sizeof(struct ip_set_req_bind))
+ return -EINVAL;
+
+ req_bind = (struct ip_set_req_bind *) data;
+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ DP("%u %s", index, req_bind->binding);
+ if (index == IP_SET_INVALID_ID) {
+ /* unbind :all: */
+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) {
+ /* Default binding of sets */
+ write_lock_bh(&ip_set_lock);
+ FOREACH_SET_DO(__unbind_default);
+ write_unlock_bh(&ip_set_lock);
+ return 0;
+ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) {
+ /* Flush all bindings of all sets*/
+ write_lock_bh(&ip_set_lock);
+ FOREACH_HASH_RW_DO(__set_hash_del);
+ write_unlock_bh(&ip_set_lock);
+ return 0;
+ }
+ DP("unreachable reached!");
+ return -EINVAL;
+ }
+
+ set = ip_set_list[index];
+ IP_SET_ASSERT(set);
+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) {
+ /* Default binding of set */
+ ip_set_id_t binding = ip_set_find_byindex(set->binding);
+
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+ write_lock_bh(&ip_set_lock);
+ /* Sets in hash values are referenced */
+ __ip_set_put(set->binding);
+ set->binding = IP_SET_INVALID_ID;
+ write_unlock_bh(&ip_set_lock);
+
+ return 0;
+ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) {
+ /* Flush all bindings */
+
+ write_lock_bh(&ip_set_lock);
+ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id);
+ write_unlock_bh(&ip_set_lock);
+ return 0;
+ }
+
+ res = __ip_set_testip(set,
+ data + sizeof(struct ip_set_req_bind),
+ size - sizeof(struct ip_set_req_bind),
+ &ip);
+
+ DP("set %s, ip: %u", set->name, ip);
+ if (res >= 0)
+ res = ip_set_hash_del(set->id, ip);
+
+ return res;
+}
+
+static int
+ip_set_testbind(ip_set_id_t index,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set = ip_set_list[index];
+ struct ip_set_req_bind *req_bind;
+ ip_set_id_t binding;
+ ip_set_ip_t ip;
+ int res;
+
+ IP_SET_ASSERT(set);
+ if (size < sizeof(struct ip_set_req_bind))
+ return -EINVAL;
+
+ req_bind = (struct ip_set_req_bind *) data;
+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) {
+ /* Default binding of set */
+ char *binding_name;
+
+ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN)
+ return -EINVAL;
+
+ binding_name = (char *)(data + sizeof(struct ip_set_req_bind));
+ binding_name[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ binding = ip_set_find_byname(binding_name);
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+ res = (set->binding == binding) ? -EEXIST : 0;
+
+ return res;
+ }
+ binding = ip_set_find_byname(req_bind->binding);
+ if (binding == IP_SET_INVALID_ID)
+ return -ENOENT;
+
+
+ res = __ip_set_testip(set,
+ data + sizeof(struct ip_set_req_bind),
+ size - sizeof(struct ip_set_req_bind),
+ &ip);
+ DP("set %s, ip: %u, binding %s",
+ set->name, ip, ip_set_list[binding]->name);
+
+ DP("set %s, ip: %u", set->name, ip);
+ if (res >= 0)
+ res = (ip_set_find_in_hash(set->id, ip) == binding)
+ ? -EEXIST : 0;
+
+ return res;
+}
+
+static struct ip_set_type *
+find_set_type_rlock(const char *typename)
+{
+ struct ip_set_type *type;
+
+ read_lock_bh(&ip_set_lock);
+ type = find_set_type(typename);
+ if (type == NULL)
+ read_unlock_bh(&ip_set_lock);
+
+ return type;
+}
+
+static int
+find_free_id(const char *name,
+ ip_set_id_t *index,
+ ip_set_id_t *id)
+{
+ ip_set_id_t i;
+
+ *id = IP_SET_INVALID_ID;
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] == NULL) {
+ if (*id == IP_SET_INVALID_ID)
+ *id = *index = i;
+ } else if (strcmp(name, ip_set_list[i]->name) == 0)
+ /* Name clash */
+ return -EEXIST;
+ }
+ if (*id == IP_SET_INVALID_ID)
+ /* No free slot remained */
+ return -ERANGE;
+ /* Check that index is usable as id (swapping) */
+ check:
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && ip_set_list[i]->id == *id) {
+ *id = i;
+ goto check;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Create a set
+ */
+static int
+ip_set_create(const char *name,
+ const char *typename,
+ ip_set_id_t restore,
+ const void *data,
+ size_t size)
+{
+ struct ip_set *set;
+ ip_set_id_t index, id;
+ int res = 0;
+
+ DP("setname: %s, typename: %s, id: %u", name, typename, restore);
+ /*
+ * First, and without any locks, allocate and initialize
+ * a normal base set structure.
+ */
+ set = kmalloc(sizeof(struct ip_set), GFP_KERNEL);
+ if (!set)
+ return -ENOMEM;
+ set->lock = RW_LOCK_UNLOCKED;
+ strncpy(set->name, name, IP_SET_MAXNAMELEN);
+ set->binding = IP_SET_INVALID_ID;
+ atomic_set(&set->ref, 0);
+
+ /*
+ * Next, take the &ip_set_lock, check that we know the type,
+ * and take a reference on the type, to make sure it
+ * stays available while constructing our new set.
+ *
+ * After referencing the type, we drop the &ip_set_lock,
+ * and let the new set construction run without locks.
+ */
+ set->type = find_set_type_rlock(typename);
+ if (set->type == NULL) {
+ /* Try loading the module */
+ char modulename[IP_SET_MAXNAMELEN + strlen("ip_set_") + 1];
+ strcpy(modulename, "ip_set_");
+ strcat(modulename, typename);
+ DP("try to load %s", modulename);
+ request_module(modulename);
+ set->type = find_set_type_rlock(typename);
+ }
+ if (set->type == NULL) {
+ ip_set_printk("no set type '%s', set '%s' not created",
+ typename, name);
+ res = -ENOENT;
+ goto out;
+ }
+ if (!try_module_get(set->type->me)) {
+ read_unlock_bh(&ip_set_lock);
+ res = -EFAULT;
+ goto out;
+ }
+ read_unlock_bh(&ip_set_lock);
+
+ /*
+ * Without holding any locks, create private part.
+ */
+ res = set->type->create(set, data, size);
+ if (res != 0)
+ goto put_out;
+
+ /* BTW, res==0 here. */
+
+ /*
+ * Here, we have a valid, constructed set. &ip_set_lock again,
+ * find free id/index and check that it is not already in
+ * ip_set_list.
+ */
+ write_lock_bh(&ip_set_lock);
+ if ((res = find_free_id(set->name, &index, &id)) != 0) {
+ DP("no free id!");
+ goto cleanup;
+ }
+
+ /* Make sure restore gets the same index */
+ if (restore != IP_SET_INVALID_ID && index != restore) {
+ DP("Can't restore, sets are screwed up");
+ res = -ERANGE;
+ goto cleanup;
+ }
+
+ /*
+ * Finally! Add our shiny new set to the list, and be done.
+ */
+ DP("create: '%s' created with index %u, id %u!", set->name, index, id);
+ set->id = id;
+ ip_set_list[index] = set;
+ write_unlock_bh(&ip_set_lock);
+ return res;
+
+ cleanup:
+ write_unlock_bh(&ip_set_lock);
+ set->type->destroy(set);
+ put_out:
+ module_put(set->type->me);
+ out:
+ kfree(set);
+ return res;
+}
+
+/*
+ * Destroy a given existing set
+ */
+static void
+ip_set_destroy_set(ip_set_id_t index)
+{
+ struct ip_set *set = ip_set_list[index];
+
+ IP_SET_ASSERT(set);
+ DP("set: %s", set->name);
+ write_lock_bh(&ip_set_lock);
+ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id);
+ if (set->binding != IP_SET_INVALID_ID)
+ __ip_set_put(set->binding);
+ ip_set_list[index] = NULL;
+ write_unlock_bh(&ip_set_lock);
+
+ /* Must call it without holding any lock */
+ set->type->destroy(set);
+ module_put(set->type->me);
+ kfree(set);
+}
+
+/*
+ * Destroy a set - or all sets
+ * Sets must not be referenced/used.
+ */
+static int
+ip_set_destroy(ip_set_id_t index)
+{
+ ip_set_id_t i;
+
+ /* ref modification always protected by the mutex */
+ if (index != IP_SET_INVALID_ID) {
+ if (atomic_read(&ip_set_list[index]->ref))
+ return -EBUSY;
+ ip_set_destroy_set(index);
+ } else {
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && (atomic_read(&ip_set_list[i]->ref)))
+ return -EBUSY;
+ }
+
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL)
+ ip_set_destroy_set(i);
+ }
+ }
+ return 0;
+}
+
+static void
+ip_set_flush_set(struct ip_set *set)
+{
+ DP("set: %s %u", set->name, set->id);
+
+ write_lock_bh(&set->lock);
+ set->type->flush(set);
+ write_unlock_bh(&set->lock);
+}
+
+/*
+ * Flush data in a set - or in all sets
+ */
+static int
+ip_set_flush(ip_set_id_t index)
+{
+ if (index != IP_SET_INVALID_ID) {
+ IP_SET_ASSERT(ip_set_list[index]);
+ ip_set_flush_set(ip_set_list[index]);
+ } else
+ FOREACH_SET_DO(ip_set_flush_set);
+
+ return 0;
+}
+
+/* Rename a set */
+static int
+ip_set_rename(ip_set_id_t index, const char *name)
+{
+ struct ip_set *set = ip_set_list[index];
+ ip_set_id_t i;
+ int res = 0;
+
+ DP("set: %s to %s", set->name, name);
+ write_lock_bh(&ip_set_lock);
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL
+ && strncmp(ip_set_list[i]->name,
+ name,
+ IP_SET_MAXNAMELEN - 1) == 0) {
+ res = -EEXIST;
+ goto unlock;
+ }
+ }
+ strncpy(set->name, name, IP_SET_MAXNAMELEN);
+ unlock:
+ write_unlock_bh(&ip_set_lock);
+ return res;
+}
+
+/*
+ * Swap two sets so that name/index points to the other.
+ * References are also swapped.
+ */
+static int
+ip_set_swap(ip_set_id_t from_index, ip_set_id_t to_index)
+{
+ struct ip_set *from = ip_set_list[from_index];
+ struct ip_set *to = ip_set_list[to_index];
+ char from_name[IP_SET_MAXNAMELEN];
+ u_int32_t from_ref;
+
+ DP("set: %s to %s", from->name, to->name);
+ /* Type can't be changed. Artifical restriction. */
+ if (from->type->typecode != to->type->typecode)
+ return -ENOEXEC;
+
+ /* No magic here: ref munging protected by the mutex */
+ write_lock_bh(&ip_set_lock);
+ strncpy(from_name, from->name, IP_SET_MAXNAMELEN);
+ from_ref = atomic_read(&from->ref);
+
+ strncpy(from->name, to->name, IP_SET_MAXNAMELEN);
+ atomic_set(&from->ref, atomic_read(&to->ref));
+ strncpy(to->name, from_name, IP_SET_MAXNAMELEN);
+ atomic_set(&to->ref, from_ref);
+
+ ip_set_list[from_index] = to;
+ ip_set_list[to_index] = from;
+
+ write_unlock_bh(&ip_set_lock);
+ return 0;
+}
+
+/*
+ * List set data
+ */
+
+static inline void
+__set_hash_bindings_size_list(struct ip_set_hash *set_hash,
+ ip_set_id_t id, size_t *size)
+{
+ if (set_hash->id == id)
+ *size += sizeof(struct ip_set_hash_list);
+}
+
+static inline void
+__set_hash_bindings_size_save(struct ip_set_hash *set_hash,
+ ip_set_id_t id, size_t *size)
+{
+ if (set_hash->id == id)
+ *size += sizeof(struct ip_set_hash_save);
+}
+
+static inline void
+__set_hash_bindings(struct ip_set_hash *set_hash,
+ ip_set_id_t id, void *data)
+{
+ if (set_hash->id == id) {
+ struct ip_set_hash_list *hash_list =
+ (struct ip_set_hash_list *)data;
+
+ hash_list->ip = set_hash->ip;
+ hash_list->binding = set_hash->binding;
+ data += sizeof(struct ip_set_hash_list);
+ }
+}
+
+static int ip_set_list_set(ip_set_id_t index,
+ void *data,
+ int *used,
+ int len)
+{
+ struct ip_set *set = ip_set_list[index];
+ struct ip_set_list *set_list;
+
+ /* Pointer to our header */
+ set_list = (struct ip_set_list *) (data + *used);
+
+ DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used);
+
+ /* Get and ensure header size */
+ if (*used + sizeof(struct ip_set_list) > len)
+ goto not_enough_mem;
+ *used += sizeof(struct ip_set_list);
+
+ read_lock_bh(&set->lock);
+ /* Get and ensure set specific header size */
+ set_list->header_size = set->type->list_header_size(set);
+ if (*used + set_list->header_size > len)
+ goto unlock_set;
+
+ /* Fill in the header */
+ set_list->index = index;
+ set_list->binding = set->binding;
+ set_list->ref = atomic_read(&set->ref);
+
+ /* Fill in set spefific header data */
+ set->type->list_header(set, data + *used);
+ *used += set_list->header_size;
+
+ /* Get and ensure set specific members size */
+ set_list->members_size = set->type->list_members_size(set);
+ if (*used + set_list->members_size > len)
+ goto unlock_set;
+
+ /* Fill in set spefific members data */
+ set->type->list_members(set, data + *used);
+ *used += set_list->members_size;
+ read_unlock_bh(&set->lock);
+
+ /* Bindings */
+
+ /* Get and ensure set specific bindings size */
+ set_list->bindings_size = 0;
+ FOREACH_HASH_DO(__set_hash_bindings_size_list,
+ set->id, &set_list->bindings_size);
+ if (*used + set_list->bindings_size > len)
+ goto not_enough_mem;
+
+ /* Fill in set spefific bindings data */
+ FOREACH_HASH_DO(__set_hash_bindings, set->id, data + *used);
+ *used += set_list->bindings_size;
+
+ return 0;
+
+ unlock_set:
+ read_unlock_bh(&set->lock);
+ not_enough_mem:
+ DP("not enough mem, try again");
+ return -EAGAIN;
+}
+
+/*
+ * Save sets
+ */
+static int ip_set_save_set(ip_set_id_t index,
+ void *data,
+ int *used,
+ int len)
+{
+ struct ip_set *set;
+ struct ip_set_save *set_save;
+
+ /* Pointer to our header */
+ set_save = (struct ip_set_save *) (data + *used);
+
+ /* Get and ensure header size */
+ if (*used + sizeof(struct ip_set_save) > len)
+ goto not_enough_mem;
+ *used += sizeof(struct ip_set_save);
+
+ set = ip_set_list[index];
+ DP("set: %s, used: %u(%u) %p %p", set->name, *used, len,
+ data, data + *used);
+
+ read_lock_bh(&set->lock);
+ /* Get and ensure set specific header size */
+ set_save->header_size = set->type->list_header_size(set);
+ if (*used + set_save->header_size > len)
+ goto unlock_set;
+
+ /* Fill in the header */
+ set_save->index = index;
+ set_save->binding = set->binding;
+
+ /* Fill in set spefific header data */
+ set->type->list_header(set, data + *used);
+ *used += set_save->header_size;
+
+ DP("set header filled: %s, used: %u %p %p", set->name, *used,
+ data, data + *used);
+ /* Get and ensure set specific members size */
+ set_save->members_size = set->type->list_members_size(set);
+ if (*used + set_save->members_size > len)
+ goto unlock_set;
+
+ /* Fill in set spefific members data */
+ set->type->list_members(set, data + *used);
+ *used += set_save->members_size;
+ read_unlock_bh(&set->lock);
+ DP("set members filled: %s, used: %u %p %p", set->name, *used,
+ data, data + *used);
+ return 0;
+
+ unlock_set:
+ read_unlock_bh(&set->lock);
+ not_enough_mem:
+ DP("not enough mem, try again");
+ return -EAGAIN;
+}
+
+static inline void
+__set_hash_save_bindings(struct ip_set_hash *set_hash,
+ ip_set_id_t id,
+ void *data,
+ int *used,
+ int len,
+ int *res)
+{
+ if (*res == 0
+ && (id == IP_SET_INVALID_ID || set_hash->id == id)) {
+ struct ip_set_hash_save *hash_save =
+ (struct ip_set_hash_save *)(data + *used);
+ /* Ensure bindings size */
+ if (*used + sizeof(struct ip_set_hash_save) > len) {
+ *res = -ENOMEM;
+ return;
+ }
+ hash_save->id = set_hash->id;
+ hash_save->ip = set_hash->ip;
+ hash_save->binding = set_hash->binding;
+ *used += sizeof(struct ip_set_hash_save);
+ }
+}
+
+static int ip_set_save_bindings(ip_set_id_t index,
+ void *data,
+ int *used,
+ int len)
+{
+ int res = 0;
+ struct ip_set_save *set_save;
+
+ DP("used %u, len %u", *used, len);
+ /* Get and ensure header size */
+ if (*used + sizeof(struct ip_set_save) > len)
+ return -ENOMEM;
+
+ /* Marker */
+ set_save = (struct ip_set_save *) (data + *used);
+ set_save->index = IP_SET_INVALID_ID;
+ *used += sizeof(struct ip_set_save);
+
+ DP("marker added used %u, len %u", *used, len);
+ /* Fill in bindings data */
+ if (index != IP_SET_INVALID_ID)
+ /* Sets are identified by id in hash */
+ index = ip_set_list[index]->id;
+ FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res);
+
+ return res;
+}
+
+/*
+ * Restore sets
+ */
+static int ip_set_restore(void *data,
+ int len)
+{
+ int res = 0;
+ int line = 0, used = 0, members_size;
+ struct ip_set *set;
+ struct ip_set_hash_save *hash_save;
+ struct ip_set_restore *set_restore;
+ ip_set_id_t index;
+
+ /* Loop to restore sets */
+ while (1) {
+ line++;
+
+ DP("%u %u %u", used, sizeof(struct ip_set_restore), len);
+ /* Get and ensure header size */
+ if (used + sizeof(struct ip_set_restore) > len)
+ return line;
+ set_restore = (struct ip_set_restore *) (data + used);
+ used += sizeof(struct ip_set_restore);
+
+ /* Ensure data size */
+ if (used
+ + set_restore->header_size
+ + set_restore->members_size > len)
+ return line;
+
+ /* Check marker */
+ if (set_restore->index == IP_SET_INVALID_ID) {
+ line--;
+ goto bindings;
+ }
+
+ /* Try to create the set */
+ DP("restore %s %s", set_restore->name, set_restore->typename);
+ res = ip_set_create(set_restore->name,
+ set_restore->typename,
+ set_restore->index,
+ data + used,
+ set_restore->header_size);
+
+ if (res != 0)
+ return line;
+ used += set_restore->header_size;
+
+ index = ip_set_find_byindex(set_restore->index);
+ DP("index %u, restore_index %u", index, set_restore->index);
+ if (index != set_restore->index)
+ return line;
+ /* Try to restore members data */
+ set = ip_set_list[index];
+ members_size = 0;
+ DP("members_size %u reqsize %u",
+ set_restore->members_size, set->type->reqsize);
+ while (members_size + set->type->reqsize <=
+ set_restore->members_size) {
+ line++;
+ DP("members: %u, line %u", members_size, line);
+ if (__ip_set_addip(index,
+ data + used + members_size,
+ set->type->reqsize)) {
+ return line;
+ }
+ members_size += set->type->reqsize;
+ }
+
+ DP("members_size %u %u",
+ set_restore->members_size, members_size);
+ if (members_size != set_restore->members_size)
+ return line++;
+ used += set_restore->members_size;
+ }
+
+ bindings:
+ /* Loop to restore bindings */
+ while (used < len) {
+ line++;
+
+ DP("restore binding, line %u", line);
+ /* Get and ensure size */
+ if (used + sizeof(struct ip_set_hash_save) > len)
+ return line;
+ hash_save = (struct ip_set_hash_save *) (data + used);
+ used += sizeof(struct ip_set_hash_save);
+
+ /* hash_save->id is used to store the index */
+ index = ip_set_find_byindex(hash_save->id);
+ DP("restore binding index %u, id %u, %u -> %u",
+ index, hash_save->id, hash_save->ip, hash_save->binding);
+ if (index != hash_save->id)
+ return line;
+
+ set = ip_set_list[hash_save->id];
+ /* Null valued IP means default binding */
+ if (hash_save->ip)
+ res = ip_set_hash_add(set->id,
+ hash_save->ip,
+ hash_save->binding);
+ else {
+ IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID);
+ write_lock_bh(&ip_set_lock);
+ set->binding = hash_save->binding;
+ __ip_set_get(set->binding);
+ write_unlock_bh(&ip_set_lock);
+ DP("default binding: %u", set->binding);
+ }
+ if (res != 0)
+ return line;
+ }
+ if (used != len)
+ return line;
+
+ return 0;
+}
+
+static int
+ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len)
+{
+ void *data;
+ int res = 0; /* Assume OK */
+ unsigned *op;
+ struct ip_set_req_adt *req_adt;
+ ip_set_id_t index = IP_SET_INVALID_ID;
+ int (*adtfn)(ip_set_id_t index,
+ const void *data, size_t size);
+ struct fn_table {
+ int (*fn)(ip_set_id_t index,
+ const void *data, size_t size);
+ } adtfn_table[] =
+ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip},
+ { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind },
+ };
+
+ DP("optval=%d, user=%p, len=%d", optval, user, len);
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (optval != SO_IP_SET)
+ return -EBADF;
+ if (len <= sizeof(unsigned)) {
+ ip_set_printk("short userdata (want >%d, got %d)",
+ sizeof(unsigned), len);
+ return -EINVAL;
+ }
+ data = vmalloc(len);
+ if (!data) {
+ DP("out of mem for %d bytes", len);
+ return -ENOMEM;
+ }
+ if (copy_from_user(data, user, len) != 0) {
+ res = -EFAULT;
+ goto done;
+ }
+ if (down_interruptible(&ip_set_app_mutex)) {
+ res = -EINTR;
+ goto done;
+ }
+
+ op = (unsigned *)data;
+ DP("op=%x", *op);
+
+ if (*op < IP_SET_OP_VERSION) {
+ /* Check the version at the beginning of operations */
+ struct ip_set_req_version *req_version =
+ (struct ip_set_req_version *) data;
+ if (req_version->version != IP_SET_PROTOCOL_VERSION) {
+ res = -EPROTO;
+ goto done;
+ }
+ }
+
+ switch (*op) {
+ case IP_SET_OP_CREATE:{
+ struct ip_set_req_create *req_create
+ = (struct ip_set_req_create *) data;
+
+ if (len <= sizeof(struct ip_set_req_create)) {
+ ip_set_printk("short CREATE data (want >%d, got %d)",
+ sizeof(struct ip_set_req_create), len);
+ res = -EINVAL;
+ goto done;
+ }
+ req_create->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ req_create->typename[IP_SET_MAXNAMELEN - 1] = '\0';
+ res = ip_set_create(req_create->name,
+ req_create->typename,
+ IP_SET_INVALID_ID,
+ data + sizeof(struct ip_set_req_create),
+ len - sizeof(struct ip_set_req_create));
+ goto done;
+ }
+ case IP_SET_OP_DESTROY:{
+ struct ip_set_req_std *req_destroy
+ = (struct ip_set_req_std *) data;
+
+ if (len != sizeof(struct ip_set_req_std)) {
+ ip_set_printk("invalid DESTROY data (want %d, got %d)",
+ sizeof(struct ip_set_req_std), len);
+ res = -EINVAL;
+ goto done;
+ }
+ if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) {
+ /* Destroy all sets */
+ index = IP_SET_INVALID_ID;
+ } else {
+ req_destroy->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byname(req_destroy->name);
+
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ }
+
+ res = ip_set_destroy(index);
+ goto done;
+ }
+ case IP_SET_OP_FLUSH:{
+ struct ip_set_req_std *req_flush =
+ (struct ip_set_req_std *) data;
+
+ if (len != sizeof(struct ip_set_req_std)) {
+ ip_set_printk("invalid FLUSH data (want %d, got %d)",
+ sizeof(struct ip_set_req_std), len);
+ res = -EINVAL;
+ goto done;
+ }
+ if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) {
+ /* Flush all sets */
+ index = IP_SET_INVALID_ID;
+ } else {
+ req_flush->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byname(req_flush->name);
+
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ }
+ res = ip_set_flush(index);
+ goto done;
+ }
+ case IP_SET_OP_RENAME:{
+ struct ip_set_req_create *req_rename
+ = (struct ip_set_req_create *) data;
+
+ if (len != sizeof(struct ip_set_req_create)) {
+ ip_set_printk("invalid RENAME data (want %d, got %d)",
+ sizeof(struct ip_set_req_create), len);
+ res = -EINVAL;
+ goto done;
+ }
+
+ req_rename->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ req_rename->typename[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ index = ip_set_find_byname(req_rename->name);
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ res = ip_set_rename(index, req_rename->typename);
+ goto done;
+ }
+ case IP_SET_OP_SWAP:{
+ struct ip_set_req_create *req_swap
+ = (struct ip_set_req_create *) data;
+ ip_set_id_t to_index;
+
+ if (len != sizeof(struct ip_set_req_create)) {
+ ip_set_printk("invalid SWAP data (want %d, got %d)",
+ sizeof(struct ip_set_req_create), len);
+ res = -EINVAL;
+ goto done;
+ }
+
+ req_swap->name[IP_SET_MAXNAMELEN - 1] = '\0';
+ req_swap->typename[IP_SET_MAXNAMELEN - 1] = '\0';
+
+ index = ip_set_find_byname(req_swap->name);
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ to_index = ip_set_find_byname(req_swap->typename);
+ if (to_index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ res = ip_set_swap(index, to_index);
+ goto done;
+ }
+ default:
+ break; /* Set identified by id */
+ }
+
+ /* There we may have add/del/test/bind/unbind/test_bind operations */
+ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) {
+ res = -EBADMSG;
+ goto done;
+ }
+ adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn;
+
+ if (len < sizeof(struct ip_set_req_adt)) {
+ ip_set_printk("short data in adt request (want >=%d, got %d)",
+ sizeof(struct ip_set_req_adt), len);
+ res = -EINVAL;
+ goto done;
+ }
+ req_adt = (struct ip_set_req_adt *) data;
+
+ /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */
+ if (!(*op == IP_SET_OP_UNBIND_SET
+ && req_adt->index == IP_SET_INVALID_ID)) {
+ index = ip_set_find_byindex(req_adt->index);
+ if (index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ }
+ res = adtfn(index, data, len);
+
+ done:
+ up(&ip_set_app_mutex);
+ vfree(data);
+ if (res > 0)
+ res = 0;
+ DP("final result %d", res);
+ return res;
+}
+
+static int
+ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len)
+{
+ int res = 0;
+ unsigned *op;
+ ip_set_id_t index = IP_SET_INVALID_ID;
+ void *data;
+ int copylen = *len;
+
+ DP("optval=%d, user=%p, len=%d", optval, user, *len);
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (optval != SO_IP_SET)
+ return -EBADF;
+ if (*len < sizeof(unsigned)) {
+ ip_set_printk("short userdata (want >=%d, got %d)",
+ sizeof(unsigned), *len);
+ return -EINVAL;
+ }
+ data = vmalloc(*len);
+ if (!data) {
+ DP("out of mem for %d bytes", *len);
+ return -ENOMEM;
+ }
+ if (copy_from_user(data, user, *len) != 0) {
+ res = -EFAULT;
+ goto done;
+ }
+ if (down_interruptible(&ip_set_app_mutex)) {
+ res = -EINTR;
+ goto done;
+ }
+
+ op = (unsigned *) data;
+ DP("op=%x", *op);
+
+ if (*op < IP_SET_OP_VERSION) {
+ /* Check the version at the beginning of operations */
+ struct ip_set_req_version *req_version =
+ (struct ip_set_req_version *) data;
+ if (req_version->version != IP_SET_PROTOCOL_VERSION) {
+ res = -EPROTO;
+ goto done;
+ }
+ }
+
+ switch (*op) {
+ case IP_SET_OP_VERSION: {
+ struct ip_set_req_version *req_version =
+ (struct ip_set_req_version *) data;
+
+ if (*len != sizeof(struct ip_set_req_version)) {
+ ip_set_printk("invalid VERSION (want %d, got %d)",
+ sizeof(struct ip_set_req_version),
+ *len);
+ res = -EINVAL;
+ goto done;
+ }
+
+ req_version->version = IP_SET_PROTOCOL_VERSION;
+ res = copy_to_user(user, req_version,
+ sizeof(struct ip_set_req_version));
+ goto done;
+ }
+ case IP_SET_OP_GET_BYNAME: {
+ struct ip_set_req_get_set *req_get
+ = (struct ip_set_req_get_set *) data;
+
+ if (*len != sizeof(struct ip_set_req_get_set)) {
+ ip_set_printk("invalid GET_BYNAME (want %d, got %d)",
+ sizeof(struct ip_set_req_get_set), *len);
+ res = -EINVAL;
+ goto done;
+ }
+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byname(req_get->set.name);
+ req_get->set.index = index;
+ goto copy;
+ }
+ case IP_SET_OP_GET_BYINDEX: {
+ struct ip_set_req_get_set *req_get
+ = (struct ip_set_req_get_set *) data;
+
+ if (*len != sizeof(struct ip_set_req_get_set)) {
+ ip_set_printk("invalid GET_BYINDEX (want %d, got %d)",
+ sizeof(struct ip_set_req_get_set), *len);
+ res = -EINVAL;
+ goto done;
+ }
+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byindex(req_get->set.index);
+ strncpy(req_get->set.name,
+ index == IP_SET_INVALID_ID ? ""
+ : ip_set_list[index]->name, IP_SET_MAXNAMELEN);
+ goto copy;
+ }
+ case IP_SET_OP_ADT_GET: {
+ struct ip_set_req_adt_get *req_get
+ = (struct ip_set_req_adt_get *) data;
+
+ if (*len != sizeof(struct ip_set_req_adt_get)) {
+ ip_set_printk("invalid ADT_GET (want %d, got %d)",
+ sizeof(struct ip_set_req_adt_get), *len);
+ res = -EINVAL;
+ goto done;
+ }
+ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+ index = ip_set_find_byname(req_get->set.name);
+ if (index != IP_SET_INVALID_ID) {
+ req_get->set.index = index;
+ strncpy(req_get->typename,
+ ip_set_list[index]->type->typename,
+ IP_SET_MAXNAMELEN - 1);
+ } else {
+ res = -ENOENT;
+ goto done;
+ }
+ goto copy;
+ }
+ case IP_SET_OP_MAX_SETS: {
+ struct ip_set_req_max_sets *req_max_sets
+ = (struct ip_set_req_max_sets *) data;
+ ip_set_id_t i;
+
+ if (*len != sizeof(struct ip_set_req_max_sets)) {
+ ip_set_printk("invalid MAX_SETS (want %d, got %d)",
+ sizeof(struct ip_set_req_max_sets), *len);
+ res = -EINVAL;
+ goto done;
+ }
+
+ if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) {
+ req_max_sets->set.index = IP_SET_INVALID_ID;
+ } else {
+ req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+ req_max_sets->set.index =
+ ip_set_find_byname(req_max_sets->set.name);
+ if (req_max_sets->set.index == IP_SET_INVALID_ID) {
+ res = -ENOENT;
+ goto done;
+ }
+ }
+ req_max_sets->max_sets = ip_set_max;
+ req_max_sets->sets = 0;
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] != NULL)
+ req_max_sets->sets++;
+ }
+ goto copy;
+ }
+ case IP_SET_OP_LIST_SIZE:
+ case IP_SET_OP_SAVE_SIZE: {
+ struct ip_set_req_setnames *req_setnames
+ = (struct ip_set_req_setnames *) data;
+ struct ip_set_name_list *name_list;
+ struct ip_set *set;
+ ip_set_id_t i;
+ int used;
+
+ if (*len < sizeof(struct ip_set_req_setnames)) {
+ ip_set_printk("short LIST_SIZE (want >=%d, got %d)",
+ sizeof(struct ip_set_req_setnames), *len);
+ res = -EINVAL;
+ goto done;
+ }
+
+ req_setnames->size = 0;
+ used = sizeof(struct ip_set_req_setnames);
+ for (i = 0; i < ip_set_max; i++) {
+ if (ip_set_list[i] == NULL)
+ continue;
+ name_list = (struct ip_set_name_list *)
+ (data + used);
+ used += sizeof(struct ip_set_name_list);
+ if (used > copylen) {
+ res = -EAGAIN;
+ goto done;
+ }
+ set = ip_set_list[i];
+ /* Fill in index, name, etc. */
+ name_list->index = i;
+ name_list->id = set->id;
+ strncpy(name_list->name,
+ set->name,
+ IP_SET_MAXNAMELEN - 1);
+ strncpy(name_list->typename,
+ set->type->typename,
+ IP_SET_MAXNAMELEN - 1);
+ DP("filled %s of type %s, index %u\n",
+ name_list->name, name_list->typename,
+ name_list->index);
+ if (!(req_setnames->index == IP_SET_INVALID_ID
+ || req_setnames->index == i))
+ continue;
+ /* Update size */
+ switch (*op) {
+ case IP_SET_OP_LIST_SIZE: {
+ req_setnames->size += sizeof(struct ip_set_list)
+ + set->type->list_header_size(set)
+ + set->type->list_members_size(set);
+ FOREACH_HASH_DO(__set_hash_bindings_size_list,
+ i, &req_setnames->size);
+ break;
+ }
+ case IP_SET_OP_SAVE_SIZE: {
+ req_setnames->size += sizeof(struct ip_set_save)
+ + set->type->list_header_size(set)
+ + set->type->list_members_size(set);
+ FOREACH_HASH_DO(__set_hash_bindings_size_save,
+ i, &req_setnames->size);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ if (copylen != used) {
+ res = -EAGAIN;
+ goto done;
+ }
+ goto copy;
+ }
+ case IP_SET_OP_LIST: {
+ struct ip_set_req_list *req_list
+ = (struct ip_set_req_list *) data;
+ ip_set_id_t i;
+ int used;
+
+ if (*len < sizeof(struct ip_set_req_list)) {
+ ip_set_printk("short LIST (want >=%d, got %d)",
+ sizeof(struct ip_set_req_list), *len);
+ res = -EINVAL;
+ goto done;
+ }
+ index = req_list->index;
+ if (index != IP_SET_INVALID_ID
+ && ip_set_find_byindex(index) != index) {
+ res = -ENOENT;
+ goto done;
+ }
+ used = 0;
+ if (index == IP_SET_INVALID_ID) {
+ /* List all sets */
+ for (i = 0; i < ip_set_max && res == 0; i++) {
+ if (ip_set_list[i] != NULL)
+ res = ip_set_list_set(i, data, &used, *len);
+ }
+ } else {
+ /* List an individual set */
+ res = ip_set_list_set(index, data, &used, *len);
+ }
+ if (res != 0)
+ goto done;
+ else if (copylen != used) {
+ res = -EAGAIN;
+ goto done;
+ }
+ goto copy;
+ }
+ case IP_SET_OP_SAVE: {
+ struct ip_set_req_list *req_save
+ = (struct ip_set_req_list *) data;
+ ip_set_id_t i;
+ int used;
+
+ if (*len < sizeof(struct ip_set_req_list)) {
+ ip_set_printk("short SAVE (want >=%d, got %d)",
+ sizeof(struct ip_set_req_list), *len);
+ res = -EINVAL;
+ goto done;
+ }
+ index = req_save->index;
+ if (index != IP_SET_INVALID_ID
+ && ip_set_find_byindex(index) != index) {
+ res = -ENOENT;
+ goto done;
+ }
+ used = 0;
+ if (index == IP_SET_INVALID_ID) {
+ /* Save all sets */
+ for (i = 0; i < ip_set_max && res == 0; i++) {
+ if (ip_set_list[i] != NULL)
+ res = ip_set_save_set(i, data, &used, *len);
+ }
+ } else {
+ /* Save an individual set */
+ res = ip_set_save_set(index, data, &used, *len);
+ }
+ if (res == 0)
+ res = ip_set_save_bindings(index, data, &used, *len);
+
+ if (res != 0)
+ goto done;
+ else if (copylen != used) {
+ res = -EAGAIN;
+ goto done;
+ }
+ goto copy;
+ }
+ case IP_SET_OP_RESTORE: {
+ struct ip_set_req_setnames *req_restore
+ = (struct ip_set_req_setnames *) data;
+ int line;
+
+ if (*len < sizeof(struct ip_set_req_setnames)
+ || *len != req_restore->size) {
+ ip_set_printk("invalid RESTORE (want =%d, got %d)",
+ req_restore->size, *len);
+ res = -EINVAL;
+ goto done;
+ }
+ line = ip_set_restore(data + sizeof(struct ip_set_req_setnames),
+ req_restore->size - sizeof(struct ip_set_req_setnames));
+ DP("ip_set_restore: %u", line);
+ if (line != 0) {
+ res = -EAGAIN;
+ req_restore->size = line;
+ copylen = sizeof(struct ip_set_req_setnames);
+ goto copy;
+ }
+ goto done;
+ }
+ default:
+ res = -EBADMSG;
+ goto done;
+ } /* end of switch(op) */
+
+ copy:
+ DP("set %s, copylen %u", index != IP_SET_INVALID_ID
+ && ip_set_list[index]
+ ? ip_set_list[index]->name
+ : ":all:", copylen);
+ if (res == 0)
+ res = copy_to_user(user, data, copylen);
+ else
+ copy_to_user(user, data, copylen);
+
+ done:
+ up(&ip_set_app_mutex);
+ vfree(data);
+ if (res > 0)
+ res = 0;
+ DP("final result %d", res);
+ return res;
+}
+
+static struct nf_sockopt_ops so_set = {
+ .pf = PF_INET,
+ .set_optmin = SO_IP_SET,
+ .set_optmax = SO_IP_SET + 1,
+ .set = &ip_set_sockfn_set,
+ .get_optmin = SO_IP_SET,
+ .get_optmax = SO_IP_SET + 1,
+ .get = &ip_set_sockfn_get,
+ .use = 0
+};
+
+static int max_sets, hash_size;
+module_param(max_sets, int, 0600);
+MODULE_PARM_DESC(max_sets, "maximal number of sets");
+module_param(hash_size, int, 0600);
+MODULE_PARM_DESC(hash_size, "hash size for bindings");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("module implementing core IP set support");
+
+static int __init init(void)
+{
+ int res;
+ ip_set_id_t i;
+
+ get_random_bytes(&ip_set_hash_random, 4);
+ if (max_sets)
+ ip_set_max = max_sets;
+ ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max);
+ if (!ip_set_list) {
+ printk(KERN_ERR "Unable to create ip_set_list\n");
+ return -ENOMEM;
+ }
+ memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max);
+ if (hash_size)
+ ip_set_bindings_hash_size = hash_size;
+ ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size);
+ if (!ip_set_hash) {
+ printk(KERN_ERR "Unable to create ip_set_hash\n");
+ vfree(ip_set_list);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ip_set_bindings_hash_size; i++)
+ INIT_LIST_HEAD(&ip_set_hash[i]);
+
+ INIT_LIST_HEAD(&set_type_list);
+
+ res = nf_register_sockopt(&so_set);
+ if (res != 0) {
+ ip_set_printk("SO_SET registry failed: %d", res);
+ vfree(ip_set_list);
+ vfree(ip_set_hash);
+ return res;
+ }
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ /* There can't be any existing set or binding */
+ nf_unregister_sockopt(&so_set);
+ vfree(ip_set_list);
+ vfree(ip_set_hash);
+ DP("these are the famous last words");
+}
+
+EXPORT_SYMBOL(ip_set_register_set_type);
+EXPORT_SYMBOL(ip_set_unregister_set_type);
+
+EXPORT_SYMBOL(ip_set_get_byname);
+EXPORT_SYMBOL(ip_set_get_byindex);
+EXPORT_SYMBOL(ip_set_put);
+
+EXPORT_SYMBOL(ip_set_addip_kernel);
+EXPORT_SYMBOL(ip_set_delip_kernel);
+EXPORT_SYMBOL(ip_set_testip_kernel);
+
+module_init(init);
+module_exit(fini);
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_iphash.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_iphash.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_iphash.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,380 @@
+/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Kernel module implementing an ip hash set */
+
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+
+#include <net/ip.h>
+
+#include <linux/netfilter_ipv4/ip_set_iphash.h>
+#include <linux/netfilter_ipv4/ip_set_jhash.h>
+#include <linux/netfilter_ipv4/ip_set_prime.h>
+
+static inline __u32
+jhash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip)
+{
+ return jhash_1word(ip, map->initval);
+}
+
+static inline __u32
+randhash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip)
+{
+ return (1 + ip % map->prime);
+}
+
+static inline __u32
+hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ __u32 jhash, randhash, id;
+ u_int16_t i;
+
+ *hash_ip = ip & map->netmask;
+ jhash = jhash_ip(map, *hash_ip);
+ randhash = randhash_ip(map, *hash_ip);
+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
+ set->name, NIPQUAD(ip), NIPQUAD(*hash_ip));
+
+ for (i = 0; i < map->probes; i++) {
+ id = (jhash + i * randhash) % map->hashsize;
+ DP("hash key: %u", id);
+ if (map->members[id] == *hash_ip)
+ return id;
+ else if (!map->members[id])
+ return UINT_MAX;
+ }
+ return UINT_MAX;
+}
+
+static inline int
+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ return (hash_id(set, ip, hash_ip) != UINT_MAX);
+}
+
+static int
+testip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
+{
+ struct ip_set_req_iphash *req =
+ (struct ip_set_req_iphash *) data;
+
+ if (size != sizeof(struct ip_set_req_iphash)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_iphash),
+ size);
+ return -EINVAL;
+ }
+ return __testip(set, req->ip, hash_ip);
+}
+
+static int
+testip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
+{
+ return __testip(set,
+ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr),
+ hash_ip);
+}
+
+static inline int
+__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ __u32 jhash, randhash, probe;
+ u_int16_t i;
+
+ *hash_ip = ip & map->netmask;
+ jhash = jhash_ip(map, *hash_ip);
+ randhash = randhash_ip(map, *hash_ip);
+
+ for (i = 0; i < map->probes; i++) {
+ probe = (jhash + i * randhash) % map->hashsize;
+ if (map->members[probe] == *hash_ip)
+ return -EEXIST;
+ if (!map->members[probe]) {
+ map->members[probe] = *hash_ip;
+ return 0;
+ }
+ }
+ /* Trigger rehashing */
+ return -EAGAIN;
+}
+
+static int
+addip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
+{
+ struct ip_set_req_iphash *req =
+ (struct ip_set_req_iphash *) data;
+
+ if (size != sizeof(struct ip_set_req_iphash)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_iphash),
+ size);
+ return -EINVAL;
+ }
+ return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip);
+}
+
+static int
+addip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
+{
+ return __addip((struct ip_set_iphash *) set->data,
+ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr),
+ hash_ip);
+}
+
+static int retry(struct ip_set *set)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ ip_set_ip_t hash_ip, *members;
+ u_int32_t i, hashsize;
+ unsigned newbytes;
+ int res;
+ struct ip_set_iphash tmp = {
+ .hashsize = map->hashsize,
+ .probes = map->probes,
+ .resize = map->resize,
+ .netmask = map->netmask,
+ };
+
+ if (map->resize == 0)
+ return -ERANGE;
+
+ again:
+ res = 0;
+
+ /* Calculate new parameters */
+ get_random_bytes(&tmp.initval, 4);
+ hashsize = tmp.hashsize + (tmp.hashsize * map->resize)/100;
+ if (hashsize == tmp.hashsize)
+ hashsize++;
+ tmp.prime = make_prime(hashsize);
+
+ ip_set_printk("rehashing of set %s triggered: "
+ "hashsize grows from %u to %u",
+ set->name, tmp.hashsize, hashsize);
+ tmp.hashsize = hashsize;
+
+ newbytes = hashsize * sizeof(ip_set_ip_t);
+ tmp.members = vmalloc(newbytes);
+ if (!tmp.members) {
+ DP("out of memory for %d bytes", newbytes);
+ return -ENOMEM;
+ }
+ memset(tmp.members, 0, newbytes);
+
+ write_lock_bh(&set->lock);
+ map = (struct ip_set_iphash *) set->data; /* Play safe */
+ for (i = 0; i < map->hashsize && res == 0; i++) {
+ if (map->members[i])
+ res = __addip(&tmp, map->members[i], &hash_ip);
+ }
+ if (res) {
+ /* Failure, try again */
+ vfree(tmp.members);
+ write_unlock_bh(&set->lock);
+ goto again;
+ }
+
+ /* Success at resizing! */
+ members = map->members;
+ map->initval = tmp.initval;
+ map->prime = tmp.prime;
+ map->hashsize = tmp.hashsize;
+ map->members = tmp.members;
+ write_unlock_bh(&set->lock);
+
+ vfree(members);
+
+ return 0;
+}
+
+static inline int
+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ ip_set_ip_t id = hash_id(set, ip, hash_ip);
+
+ if (id == UINT_MAX)
+ return -EEXIST;
+
+ map->members[id] = 0;
+ return 0;
+}
+
+static int
+delip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
+{
+ struct ip_set_req_iphash *req =
+ (struct ip_set_req_iphash *) data;
+
+ if (size != sizeof(struct ip_set_req_iphash)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_iphash),
+ size);
+ return -EINVAL;
+ }
+ return __delip(set, req->ip, hash_ip);
+}
+
+static int
+delip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
+{
+ return __delip(set,
+ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr),
+ hash_ip);
+}
+
+static int create(struct ip_set *set, const void *data, size_t size)
+{
+ unsigned newbytes;
+ struct ip_set_req_iphash_create *req =
+ (struct ip_set_req_iphash_create *) data;
+ struct ip_set_iphash *map;
+
+ if (size != sizeof(struct ip_set_req_iphash_create)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_iphash_create),
+ size);
+ return -EINVAL;
+ }
+
+ if (req->hashsize < 1) {
+ ip_set_printk("hashsize too small");
+ return -ENOEXEC;
+ }
+
+ map = kmalloc(sizeof(struct ip_set_iphash), GFP_KERNEL);
+ if (!map) {
+ DP("out of memory for %d bytes",
+ sizeof(struct ip_set_iphash));
+ return -ENOMEM;
+ }
+ get_random_bytes(&map->initval, 4);
+ map->prime = make_prime(req->hashsize);
+ map->hashsize = req->hashsize;
+ map->probes = req->probes;
+ map->resize = req->resize;
+ map->netmask = req->netmask;
+ newbytes = map->hashsize * sizeof(ip_set_ip_t);
+ map->members = vmalloc(newbytes);
+ if (!map->members) {
+ DP("out of memory for %d bytes", newbytes);
+ kfree(map);
+ return -ENOMEM;
+ }
+ memset(map->members, 0, newbytes);
+
+ set->data = map;
+ return 0;
+}
+
+static void destroy(struct ip_set *set)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+
+ vfree(map->members);
+ kfree(map);
+
+ set->data = NULL;
+}
+
+static void flush(struct ip_set *set)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ memset(map->members, 0, map->hashsize * sizeof(ip_set_ip_t));
+}
+
+static int list_header_size(const struct ip_set *set)
+{
+ return sizeof(struct ip_set_req_iphash_create);
+}
+
+static void list_header(const struct ip_set *set, void *data)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ struct ip_set_req_iphash_create *header =
+ (struct ip_set_req_iphash_create *) data;
+
+ header->hashsize = map->hashsize;
+ header->probes = map->probes;
+ header->resize = map->resize;
+ header->netmask = map->netmask;
+}
+
+static int list_members_size(const struct ip_set *set)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+
+ return (map->hashsize * sizeof(ip_set_ip_t));
+}
+
+static void list_members(const struct ip_set *set, void *data)
+{
+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data;
+ int bytes = map->hashsize * sizeof(ip_set_ip_t);
+
+ memcpy(data, map->members, bytes);
+}
+
+static struct ip_set_type ip_set_iphash = {
+ .typename = SETTYPE_NAME,
+ .typecode = IPSET_TYPE_IP,
+ .protocol_version = IP_SET_PROTOCOL_VERSION,
+ .create = &create,
+ .destroy = &destroy,
+ .flush = &flush,
+ .reqsize = sizeof(struct ip_set_req_iphash),
+ .addip = &addip,
+ .addip_kernel = &addip_kernel,
+ .retry = &retry,
+ .delip = &delip,
+ .delip_kernel = &delip_kernel,
+ .testip = &testip,
+ .testip_kernel = &testip_kernel,
+ .list_header_size = &list_header_size,
+ .list_header = &list_header,
+ .list_members_size = &list_members_size,
+ .list_members = &list_members,
+ .me = THIS_MODULE,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("iphash type of IP sets");
+
+static int __init init(void)
+{
+ return ip_set_register_set_type(&ip_set_iphash);
+}
+
+static void __exit fini(void)
+{
+ /* FIXME: possible race with ip_set_create() */
+ ip_set_unregister_set_type(&ip_set_iphash);
+}
+
+module_init(init);
+module_exit(fini);
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_ipmap.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_ipmap.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_ipmap.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,318 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Kernel module implementing an IP set type: the single bitmap type */
+
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <linux/spinlock.h>
+
+#include <linux/netfilter_ipv4/ip_set_ipmap.h>
+
+static inline ip_set_ip_t
+ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip)
+{
+ return (ip - map->first_ip)/map->hosts;
+}
+
+static inline int
+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
+
+ if (ip < map->first_ip || ip > map->last_ip)
+ return -ERANGE;
+
+ *hash_ip = ip & map->netmask;
+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
+ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip));
+ return !!test_bit(ip_to_id(map, *hash_ip), map->members);
+}
+
+static int
+testip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
+{
+ struct ip_set_req_ipmap *req =
+ (struct ip_set_req_ipmap *) data;
+
+ if (size != sizeof(struct ip_set_req_ipmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_ipmap),
+ size);
+ return -EINVAL;
+ }
+ return __testip(set, req->ip, hash_ip);
+}
+
+static int
+testip_kernel(struct ip_set *set,
+ const struct sk_buff *skb,
+ u_int32_t flags,
+ ip_set_ip_t *hash_ip)
+{
+ int res;
+
+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u",
+ flags & IPSET_SRC ? "SRC" : "DST",
+ NIPQUAD(skb->nh.iph->saddr),
+ NIPQUAD(skb->nh.iph->daddr));
+
+ res = __testip(set,
+ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr),
+ hash_ip);
+ return (res < 0 ? 0 : res);
+}
+
+static inline int
+__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
+
+ if (ip < map->first_ip || ip > map->last_ip)
+ return -ERANGE;
+
+ *hash_ip = ip & map->netmask;
+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip));
+ if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members))
+ return -EEXIST;
+
+ return 0;
+}
+
+static int
+addip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
+{
+ struct ip_set_req_ipmap *req =
+ (struct ip_set_req_ipmap *) data;
+
+ if (size != sizeof(struct ip_set_req_ipmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_ipmap),
+ size);
+ return -EINVAL;
+ }
+ DP("%u.%u.%u.%u", HIPQUAD(req->ip));
+ return __addip(set, req->ip, hash_ip);
+}
+
+static int
+addip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
+{
+ return __addip(set,
+ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr),
+ hash_ip);
+}
+
+static inline int
+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
+
+ if (ip < map->first_ip || ip > map->last_ip)
+ return -ERANGE;
+
+ *hash_ip = ip & map->netmask;
+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip));
+ if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members))
+ return -EEXIST;
+
+ return 0;
+}
+
+static int
+delip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
+{
+ struct ip_set_req_ipmap *req =
+ (struct ip_set_req_ipmap *) data;
+
+ if (size != sizeof(struct ip_set_req_ipmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_ipmap),
+ size);
+ return -EINVAL;
+ }
+ return __delip(set, req->ip, hash_ip);
+}
+
+static int
+delip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
+{
+ return __delip(set,
+ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr),
+ hash_ip);
+}
+
+static int create(struct ip_set *set, const void *data, size_t size)
+{
+ int newbytes;
+ struct ip_set_req_ipmap_create *req =
+ (struct ip_set_req_ipmap_create *) data;
+ struct ip_set_ipmap *map;
+
+ if (size != sizeof(struct ip_set_req_ipmap_create)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_ipmap_create),
+ size);
+ return -EINVAL;
+ }
+
+ DP("from %u.%u.%u.%u to %u.%u.%u.%u",
+ HIPQUAD(req->from), HIPQUAD(req->to));
+
+ if (req->from > req->to) {
+ DP("bad ip range");
+ return -ENOEXEC;
+ }
+
+ if (req->to - req->from > MAX_RANGE) {
+ ip_set_printk("range too big (max %d addresses)",
+ MAX_RANGE);
+ return -ENOEXEC;
+ }
+
+ map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL);
+ if (!map) {
+ DP("out of memory for %d bytes",
+ sizeof(struct ip_set_ipmap));
+ return -ENOMEM;
+ }
+ map->first_ip = req->from;
+ map->last_ip = req->to;
+ map->netmask = req->netmask;
+
+ if (req->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;
+
+ map->first_ip &= map->netmask; /* Should we better bark? */
+
+ mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits);
+ netmask_bits = mask_to_bits(map->netmask);
+
+ if (!mask || netmask_bits <= mask_bits)
+ return -ENOEXEC;
+
+ map->hosts = 2 << (32 - netmask_bits - 1);
+ map->sizeid = 2 << (netmask_bits - mask_bits - 1);
+ }
+ newbytes = bitmap_bytes(0, map->sizeid - 1);
+ map->members = kmalloc(newbytes, GFP_KERNEL);
+ if (!map->members) {
+ DP("out of memory for %d bytes", newbytes);
+ kfree(map);
+ return -ENOMEM;
+ }
+ memset(map->members, 0, newbytes);
+
+ set->data = map;
+ return 0;
+}
+
+static void destroy(struct ip_set *set)
+{
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
+
+ kfree(map->members);
+ kfree(map);
+
+ set->data = NULL;
+}
+
+static void flush(struct ip_set *set)
+{
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
+ memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1));
+}
+
+static int list_header_size(const struct ip_set *set)
+{
+ return sizeof(struct ip_set_req_ipmap_create);
+}
+
+static void list_header(const struct ip_set *set, void *data)
+{
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
+ struct ip_set_req_ipmap_create *header =
+ (struct ip_set_req_ipmap_create *) data;
+
+ header->from = map->first_ip;
+ header->to = map->last_ip;
+ header->netmask = map->netmask;
+}
+
+static int list_members_size(const struct ip_set *set)
+{
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
+
+ return bitmap_bytes(0, map->sizeid - 1);
+}
+
+static void list_members(const struct ip_set *set, void *data)
+{
+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
+ int bytes = bitmap_bytes(0, map->sizeid - 1);
+
+ memcpy(data, map->members, bytes);
+}
+
+static struct ip_set_type ip_set_ipmap = {
+ .typename = SETTYPE_NAME,
+ .typecode = IPSET_TYPE_IP,
+ .protocol_version = IP_SET_PROTOCOL_VERSION,
+ .create = &create,
+ .destroy = &destroy,
+ .flush = &flush,
+ .reqsize = sizeof(struct ip_set_req_ipmap),
+ .addip = &addip,
+ .addip_kernel = &addip_kernel,
+ .delip = &delip,
+ .delip_kernel = &delip_kernel,
+ .testip = &testip,
+ .testip_kernel = &testip_kernel,
+ .list_header_size = &list_header_size,
+ .list_header = &list_header,
+ .list_members_size = &list_members_size,
+ .list_members = &list_members,
+ .me = THIS_MODULE,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("ipmap type of IP sets");
+
+static int __init init(void)
+{
+ return ip_set_register_set_type(&ip_set_ipmap);
+}
+
+static void __exit fini(void)
+{
+ /* FIXME: possible race with ip_set_create() */
+ ip_set_unregister_set_type(&ip_set_ipmap);
+}
+
+module_init(init);
+module_exit(fini);
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_macipmap.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_macipmap.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_macipmap.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,341 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Martin Josefsson <gandalf at wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Kernel module implementing an IP set type: the macipmap type */
+
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/if_ether.h>
+#include <linux/vmalloc.h>
+
+#include <linux/netfilter_ipv4/ip_set_macipmap.h>
+
+static int
+testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data;
+ struct ip_set_macip *table = (struct ip_set_macip *) map->members;
+ struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data;
+
+ if (size != sizeof(struct ip_set_req_macipmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_macipmap),
+ size);
+ return -EINVAL;
+ }
+
+ if (req->ip < map->first_ip || req->ip > map->last_ip)
+ return -ERANGE;
+
+ *hash_ip = req->ip;
+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
+ set->name, NIPQUAD(req->ip), NIPQUAD(*hash_ip));
+ if (test_bit(IPSET_MACIP_ISSET,
+ (void *) &table[req->ip - map->first_ip].flags)) {
+ /* Is mac pointer valid?
+ * If so, compare... */
+ return (memcmp(req->ethernet,
+ &table[req->ip - map->first_ip].ethernet,
+ ETH_ALEN) == 0);
+ } else {
+ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0);
+ }
+}
+
+static int
+testip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_macipmap *map =
+ (struct ip_set_macipmap *) set->data;
+ struct ip_set_macip *table =
+ (struct ip_set_macip *) map->members;
+ ip_set_ip_t ip;
+
+ ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr);
+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u",
+ flags & IPSET_SRC ? "SRC" : "DST",
+ NIPQUAD(skb->nh.iph->saddr),
+ NIPQUAD(skb->nh.iph->daddr));
+
+ if (ip < map->first_ip || ip > map->last_ip)
+ return 0;
+
+ *hash_ip = ip;
+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
+ set->name, NIPQUAD(ip), NIPQUAD(*hash_ip));
+ if (test_bit(IPSET_MACIP_ISSET,
+ (void *) &table[ip - map->first_ip].flags)) {
+ /* Is mac pointer valid?
+ * If so, compare... */
+ return (skb->mac.raw >= skb->head
+ && (skb->mac.raw + ETH_HLEN) <= skb->data
+ && (memcmp(skb->mac.ethernet->h_source,
+ &table[ip - map->first_ip].ethernet,
+ ETH_ALEN) == 0));
+ } else {
+ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0);
+ }
+}
+
+/* returns 0 on success */
+static inline int
+__addip(struct ip_set *set,
+ ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_macipmap *map =
+ (struct ip_set_macipmap *) set->data;
+ struct ip_set_macip *table =
+ (struct ip_set_macip *) map->members;
+
+ if (ip < map->first_ip || ip > map->last_ip)
+ return -ERANGE;
+ if (test_and_set_bit(IPSET_MACIP_ISSET,
+ (void *) &table[ip - map->first_ip].flags))
+ return -EEXIST;
+
+ *hash_ip = ip;
+ DP("%u.%u.%u.%u, %u.%u.%u.%u", NIPQUAD(ip), NIPQUAD(*hash_ip));
+ memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN);
+ return 0;
+}
+
+static int
+addip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
+{
+ struct ip_set_req_macipmap *req =
+ (struct ip_set_req_macipmap *) data;
+
+ if (size != sizeof(struct ip_set_req_macipmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_macipmap),
+ size);
+ return -EINVAL;
+ }
+ return __addip(set, req->ip, req->ethernet, hash_ip);
+}
+
+static int
+addip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
+{
+ ip_set_ip_t ip;
+
+ ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr);
+
+ if (!(skb->mac.raw >= skb->head
+ && (skb->mac.raw + ETH_HLEN) <= skb->data))
+ return -EINVAL;
+
+ return __addip(set, ip, skb->mac.ethernet->h_source, hash_ip);
+}
+
+static inline int
+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+{
+ struct ip_set_macipmap *map =
+ (struct ip_set_macipmap *) set->data;
+ struct ip_set_macip *table =
+ (struct ip_set_macip *) map->members;
+
+ if (ip < map->first_ip || ip > map->last_ip)
+ return -ERANGE;
+ if (!test_and_clear_bit(IPSET_MACIP_ISSET,
+ (void *)&table[ip - map->first_ip].flags))
+ return -EEXIST;
+
+ *hash_ip = ip;
+ DP("%u.%u.%u.%u, %u.%u.%u.%u", NIPQUAD(ip), NIPQUAD(*hash_ip));
+ return 0;
+}
+
+static int
+delip(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_ip)
+{
+ struct ip_set_req_macipmap *req =
+ (struct ip_set_req_macipmap *) data;
+
+ if (size != sizeof(struct ip_set_req_macipmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_macipmap),
+ size);
+ return -EINVAL;
+ }
+ return __delip(set, req->ip, hash_ip);
+}
+
+static int
+delip_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_ip)
+{
+ return __delip(set,
+ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr
+ : skb->nh.iph->daddr),
+ hash_ip);
+}
+
+static int create(struct ip_set *set, const void *data, size_t size)
+{
+ int newbytes;
+ struct ip_set_req_macipmap_create *req =
+ (struct ip_set_req_macipmap_create *) data;
+ struct ip_set_macipmap *map;
+
+ if (size != sizeof(struct ip_set_req_macipmap_create)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_macipmap_create),
+ size);
+ return -EINVAL;
+ }
+
+ DP("from %u.%u.%u.%u to %u.%u.%u.%u",
+ NIPQUAD(req->from), NIPQUAD(req->to));
+
+ if (req->from > req->to) {
+ DP("bad ip range");
+ return -ENOEXEC;
+ }
+
+ if (req->to - req->from > MAX_RANGE) {
+ ip_set_printk("range too big (max %d addresses)",
+ MAX_RANGE);
+ return -ENOEXEC;
+ }
+
+ map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL);
+ if (!map) {
+ DP("out of memory for %d bytes",
+ sizeof(struct ip_set_macipmap));
+ return -ENOMEM;
+ }
+ map->flags = req->flags;
+ map->first_ip = req->from;
+ map->last_ip = req->to;
+ newbytes = (req->to - req->from + 1) * sizeof(struct ip_set_macip);
+ map->members = vmalloc(newbytes);
+ if (!map->members) {
+ DP("out of memory for %d bytes", newbytes);
+ kfree(map);
+ return -ENOMEM;
+ }
+ memset(map->members, 0, newbytes);
+
+ set->data = map;
+ return 0;
+}
+
+static void destroy(struct ip_set *set)
+{
+ struct ip_set_macipmap *map =
+ (struct ip_set_macipmap *) set->data;
+
+ vfree(map->members);
+ kfree(map);
+
+ set->data = NULL;
+}
+
+static void flush(struct ip_set *set)
+{
+ struct ip_set_macipmap *map =
+ (struct ip_set_macipmap *) set->data;
+ memset(map->members, 0, (map->last_ip - map->first_ip)
+ * sizeof(struct ip_set_macip));
+}
+
+static int list_header_size(const struct ip_set *set)
+{
+ return sizeof(struct ip_set_req_macipmap_create);
+}
+
+static void list_header(const struct ip_set *set, void *data)
+{
+ struct ip_set_macipmap *map =
+ (struct ip_set_macipmap *) set->data;
+ struct ip_set_req_macipmap_create *header =
+ (struct ip_set_req_macipmap_create *) data;
+
+ DP("list_header %x %x %u", map->first_ip, map->last_ip,
+ map->flags);
+
+ header->from = map->first_ip;
+ header->to = map->last_ip;
+ header->flags = map->flags;
+}
+
+static int list_members_size(const struct ip_set *set)
+{
+ struct ip_set_macipmap *map =
+ (struct ip_set_macipmap *) set->data;
+
+ return (map->last_ip
+ - map->first_ip + 1) * sizeof(struct ip_set_macip);
+}
+
+static void list_members(const struct ip_set *set, void *data)
+{
+ struct ip_set_macipmap *map =
+ (struct ip_set_macipmap *) set->data;
+
+ int bytes = (map->last_ip -
+ - map->first_ip + 1) * sizeof(struct ip_set_macip);
+
+ memcpy(data, map->members, bytes);
+}
+
+static struct ip_set_type ip_set_macipmap = {
+ .typename = SETTYPE_NAME,
+ .typecode = IPSET_TYPE_IP,
+ .protocol_version = IP_SET_PROTOCOL_VERSION,
+ .create = &create,
+ .destroy = &destroy,
+ .flush = &flush,
+ .reqsize = sizeof(struct ip_set_req_macipmap),
+ .addip = &addip,
+ .addip_kernel = &addip_kernel,
+ .delip = &delip,
+ .delip_kernel = &delip_kernel,
+ .testip = &testip,
+ .testip_kernel = &testip_kernel,
+ .list_header_size = &list_header_size,
+ .list_header = &list_header,
+ .list_members_size = &list_members_size,
+ .list_members = &list_members,
+ .me = THIS_MODULE,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("macipmap type of IP sets");
+
+static int __init init(void)
+{
+ return ip_set_register_set_type(&ip_set_macipmap);
+}
+
+static void __exit fini(void)
+{
+ /* FIXME: possible race with ip_set_create() */
+ ip_set_unregister_set_type(&ip_set_macipmap);
+}
+
+module_init(init);
+module_exit(fini);
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_portmap.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_portmap.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ip_set_portmap.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,330 @@
+/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Kernel module implementing a port set type as a bitmap */
+
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <linux/spinlock.h>
+
+#include <net/ip.h>
+
+#include <linux/netfilter_ipv4/ip_set_portmap.h>
+
+/* We must handle non-linear skbs */
+static inline ip_set_ip_t
+get_port(const struct sk_buff *skb, u_int32_t flags)
+{
+ struct iphdr *iph = skb->nh.iph;
+ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET;
+
+ switch (iph->protocol) {
+ case IPPROTO_TCP: {
+ struct tcphdr tcph;
+
+ /* See comments at tcp_match in ip_tables.c */
+ if (offset)
+ return INVALID_PORT;
+
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0)
+ /* No choice either */
+ return INVALID_PORT;
+
+ return ntohs(flags & IPSET_SRC ?
+ tcph.source : tcph.dest);
+ }
+ case IPPROTO_UDP: {
+ struct udphdr udph;
+
+ if (offset)
+ return INVALID_PORT;
+
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0)
+ /* No choice either */
+ return INVALID_PORT;
+
+ return ntohs(flags & IPSET_SRC ?
+ udph.source : udph.dest);
+ }
+ default:
+ return INVALID_PORT;
+ }
+}
+
+static inline int
+__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port)
+{
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
+
+ if (port < map->first_port || port > map->last_port)
+ return -ERANGE;
+
+ *hash_port = port;
+ DP("set: %s, port:%u, %u", set->name, port, *hash_port);
+ return !!test_bit(port - map->first_port, map->members);
+}
+
+static int
+testport(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_port)
+{
+ struct ip_set_req_portmap *req =
+ (struct ip_set_req_portmap *) data;
+
+ if (size != sizeof(struct ip_set_req_portmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_portmap),
+ size);
+ return -EINVAL;
+ }
+ return __testport(set, req->port, hash_port);
+}
+
+static int
+testport_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_port)
+{
+ int res;
+ ip_set_ip_t port = get_port(skb, flags);
+
+ DP("flag %s port %u", flags & IPSET_SRC ? "SRC" : "DST", port);
+ if (port == INVALID_PORT)
+ return 0;
+
+ res = __testport(set, port, hash_port);
+
+ return (res < 0 ? 0 : res);
+}
+
+static inline int
+__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port)
+{
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
+
+ if (port < map->first_port || port > map->last_port)
+ return -ERANGE;
+ if (test_and_set_bit(port - map->first_port, map->members))
+ return -EEXIST;
+
+ *hash_port = port;
+ DP("port %u", port);
+ return 0;
+}
+
+static int
+addport(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_port)
+{
+ struct ip_set_req_portmap *req =
+ (struct ip_set_req_portmap *) data;
+
+ if (size != sizeof(struct ip_set_req_portmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_portmap),
+ size);
+ return -EINVAL;
+ }
+ return __addport(set, req->port, hash_port);
+}
+
+static int
+addport_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_port)
+{
+ ip_set_ip_t port = get_port(skb, flags);
+
+ if (port == INVALID_PORT)
+ return -EINVAL;
+
+ return __addport(set, port, hash_port);
+}
+
+static inline int
+__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port)
+{
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
+
+ if (port < map->first_port || port > map->last_port)
+ return -ERANGE;
+ if (!test_and_clear_bit(port - map->first_port, map->members))
+ return -EEXIST;
+
+ *hash_port = port;
+ DP("port %u", port);
+ return 0;
+}
+
+static int
+delport(struct ip_set *set, const void *data, size_t size,
+ ip_set_ip_t *hash_port)
+{
+ struct ip_set_req_portmap *req =
+ (struct ip_set_req_portmap *) data;
+
+ if (size != sizeof(struct ip_set_req_portmap)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_portmap),
+ size);
+ return -EINVAL;
+ }
+ return __delport(set, req->port, hash_port);
+}
+
+static int
+delport_kernel(struct ip_set *set, const struct sk_buff *skb,
+ u_int32_t flags, ip_set_ip_t *hash_port)
+{
+ ip_set_ip_t port = get_port(skb, flags);
+
+ if (port == INVALID_PORT)
+ return -EINVAL;
+
+ return __delport(set, port, hash_port);
+}
+
+static int create(struct ip_set *set, const void *data, size_t size)
+{
+ int newbytes;
+ struct ip_set_req_portmap_create *req =
+ (struct ip_set_req_portmap_create *) data;
+ struct ip_set_portmap *map;
+
+ if (size != sizeof(struct ip_set_req_portmap_create)) {
+ ip_set_printk("data length wrong (want %d, have %d)",
+ sizeof(struct ip_set_req_portmap_create),
+ size);
+ return -EINVAL;
+ }
+
+ DP("from %u to %u", req->from, req->to);
+
+ if (req->from > req->to) {
+ DP("bad port range");
+ return -ENOEXEC;
+ }
+
+ if (req->to - req->from > MAX_RANGE) {
+ ip_set_printk("range too big (max %d ports)",
+ MAX_RANGE);
+ return -ENOEXEC;
+ }
+
+ map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL);
+ if (!map) {
+ DP("out of memory for %d bytes",
+ sizeof(struct ip_set_portmap));
+ return -ENOMEM;
+ }
+ map->first_port = req->from;
+ map->last_port = req->to;
+ newbytes = bitmap_bytes(req->from, req->to);
+ map->members = kmalloc(newbytes, GFP_KERNEL);
+ if (!map->members) {
+ DP("out of memory for %d bytes", newbytes);
+ kfree(map);
+ return -ENOMEM;
+ }
+ memset(map->members, 0, newbytes);
+
+ set->data = map;
+ return 0;
+}
+
+static void destroy(struct ip_set *set)
+{
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
+
+ kfree(map->members);
+ kfree(map);
+
+ set->data = NULL;
+}
+
+static void flush(struct ip_set *set)
+{
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
+ memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port));
+}
+
+static int list_header_size(const struct ip_set *set)
+{
+ return sizeof(struct ip_set_req_portmap_create);
+}
+
+static void list_header(const struct ip_set *set, void *data)
+{
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
+ struct ip_set_req_portmap_create *header =
+ (struct ip_set_req_portmap_create *) data;
+
+ DP("list_header %u %u", map->first_port, map->last_port);
+
+ header->from = map->first_port;
+ header->to = map->last_port;
+}
+
+static int list_members_size(const struct ip_set *set)
+{
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
+
+ return bitmap_bytes(map->first_port, map->last_port);
+}
+
+static void list_members(const struct ip_set *set, void *data)
+{
+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data;
+ int bytes = bitmap_bytes(map->first_port, map->last_port);
+
+ memcpy(data, map->members, bytes);
+}
+
+static struct ip_set_type ip_set_portmap = {
+ .typename = SETTYPE_NAME,
+ .typecode = IPSET_TYPE_PORT,
+ .protocol_version = IP_SET_PROTOCOL_VERSION,
+ .create = &create,
+ .destroy = &destroy,
+ .flush = &flush,
+ .reqsize = sizeof(struct ip_set_req_portmap),
+ .addip = &addport,
+ .addip_kernel = &addport_kernel,
+ .delip = &delport,
+ .delip_kernel = &delport_kernel,
+ .testip = &testport,
+ .testip_kernel = &testport_kernel,
+ .list_header_size = &list_header_size,
+ .list_header = &list_header,
+ .list_members_size = &list_members_size,
+ .list_members = &list_members,
+ .me = THIS_MODULE,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("portmap type of IP sets");
+
+static int __init init(void)
+{
+ return ip_set_register_set_type(&ip_set_portmap);
+}
+
+static void __exit fini(void)
+{
+ /* FIXME: possible race with ip_set_create() */
+ ip_set_unregister_set_type(&ip_set_portmap);
+}
+
+module_init(init);
+module_exit(fini);
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ipt_SET.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ipt_SET.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ipt_SET.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,123 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Martin Josefsson <gandalf at wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* ipt_SET.c - netfilter target to manipulate IP sets */
+
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/timer.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <net/protocol.h>
+#include <net/checksum.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ipt_set.h>
+
+static unsigned int
+target(struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ unsigned int hooknum,
+ const void *targinfo,
+ void *userinfo)
+{
+ const struct ipt_set_info_target *info = targinfo;
+
+ if (info->add_set.index != IP_SET_INVALID_ID)
+ ip_set_addip_kernel(info->add_set.index,
+ *pskb,
+ info->add_set.flags);
+ if (info->del_set.index != IP_SET_INVALID_ID)
+ ip_set_delip_kernel(info->del_set.index,
+ *pskb,
+ info->del_set.flags);
+
+ return IPT_CONTINUE;
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize, unsigned int hook_mask)
+{
+ struct ipt_set_info_target *info =
+ (struct ipt_set_info_target *) targinfo;
+ ip_set_id_t index;
+
+ if (targinfosize != IPT_ALIGN(sizeof(*info))) {
+ DP("bad target info size %u", targinfosize);
+ return 0;
+ }
+
+ if (info->add_set.index != IP_SET_INVALID_ID) {
+ index = ip_set_get_byindex(info->add_set.index);
+ if (index == IP_SET_INVALID_ID) {
+ ip_set_printk("cannot find add_set index %u as target",
+ info->add_set.index);
+ return 0; /* error */
+ }
+ }
+
+ if (info->del_set.index != IP_SET_INVALID_ID) {
+ index = ip_set_get_byindex(info->del_set.index);
+ if (index == IP_SET_INVALID_ID) {
+ ip_set_printk("cannot find del_set index %u as target",
+ info->del_set.index);
+ return 0; /* error */
+ }
+ }
+
+ return 1;
+}
+
+static void destroy(void *targetinfo, unsigned int targetsize)
+{
+ struct ipt_set_info_target *info = targetinfo;
+
+ if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) {
+ ip_set_printk("invalid targetsize %d", targetsize);
+ return;
+ }
+
+ if (info->add_set.index != IP_SET_INVALID_ID)
+ ip_set_put(info->add_set.index);
+ if (info->del_set.index != IP_SET_INVALID_ID)
+ ip_set_put(info->del_set.index);
+}
+
+static struct ipt_target SET_target = {
+ .name = "SET",
+ .target = target,
+ .checkentry = checkentry,
+ .destroy = destroy,
+ .me = THIS_MODULE
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("iptables IP set target module");
+
+static int __init init(void)
+{
+ return ipt_register_target(&SET_target);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&SET_target);
+}
+
+module_init(init);
+module_exit(fini);
Added: trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ipt_set.c
===================================================================
--- trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ipt_set.c 2004-12-01 09:11:33 UTC (rev 3304)
+++ trunk/patch-o-matic-ng/set/linux-2.6/net/ipv4/netfilter/ipt_set.c 2004-12-01 09:49:36 UTC (rev 3305)
@@ -0,0 +1,108 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem at linux.nu>
+ * Patrick Schaaf <bof at bof.de>
+ * Martin Josefsson <gandalf at wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Kernel module to match an IP set. */
+
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+#include <linux/netfilter_ipv4/ipt_set.h>
+
+static inline int
+match_set(const struct ipt_set_info *info,
+ const struct sk_buff *skb,
+ int inv)
+{
+ if (ip_set_testip_kernel(info->index, skb, info->flags))
+ inv = !inv;
+ return inv;
+}
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ int *hotdrop)
+{
+ const struct ipt_set_info_match *info = matchinfo;
+
+ return match_set(&info->match_set,
+ skb,
+ info->match_set.flags[0] & IPSET_MATCH_INV);
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ struct ipt_set_info_match *info =
+ (struct ipt_set_info_match *) matchinfo;
+ ip_set_id_t index;
+
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) {
+ ip_set_printk("invalid matchsize %d", matchsize);
+ return 0;
+ }
+
+ index = ip_set_get_byindex(info->match_set.index);
+
+ if (index == IP_SET_INVALID_ID) {
+ ip_set_printk("Cannot find set indentified by id %u to match",
+ info->match_set.index);
+ return 0; /* error */
+ }
+
+ return 1;
+}
+
+static void destroy(void *matchinfo, unsigned int matchsize)
+{
+ struct ipt_set_info_match *info = matchinfo;
+
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) {
+ ip_set_printk("invalid matchsize %d", matchsize);
+ return;
+ }
+
+ ip_set_put(info->match_set.index);
+}
+
+static struct ipt_match set_match = {
+ .name = "set",
+ .match = &match,
+ .checkentry = &checkentry,
+ .destroy = &destroy,
+ .me = THIS_MODULE
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec at blackhole.kfki.hu>");
+MODULE_DESCRIPTION("iptables IP set match module");
+
+static int __init init(void)
+{
+ return ipt_register_match(&set_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&set_match);
+}
+
+module_init(init);
+module_exit(fini);
More information about the netfilter-cvslog
mailing list