summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-06-15 13:30:55 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-06-15 13:30:55 +0200
commit3fd6b24ace319b139ec3c4e3031a5f05d21e304e (patch)
treee6ac952e95fa44968196149e0172b1ef13e8236f
parent00bcb2b40450eca4c7ad785bf85b12692e8d29af (diff)
ipset 5 in an almost ready state - milestonev5.0-pre1
Reworked protocol and internal interfaces, missing set types added, backward compatibility verified, lots of tests added (and thanks to the tests, bugs fixed), even the manpage is rewritten ;-). Countless changes everywhere... The missing bits before announcing ipset 5: - net namespace support - new iptables/ip6tables extension library - iptables/ip6tables match and target tests (backward/forward compatibility) - tests on catching syntax errors
-rw-r--r--Make_global.am2
-rw-r--r--Makefile.am1
-rw-r--r--include/libipset/data.h11
-rw-r--r--include/libipset/debug.h35
-rw-r--r--include/libipset/linux_ip_set.h51
-rw-r--r--include/libipset/linux_ip_set_list.h14
-rw-r--r--include/libipset/parse.h24
-rw-r--r--include/libipset/print.h6
-rw-r--r--include/libipset/session.h22
-rw-r--r--include/libipset/types.h36
-rw-r--r--include/libipset/ui.h24
-rw-r--r--include/libipset/utils.h6
-rw-r--r--kernel/Kbuild11
-rw-r--r--kernel/include/linux/netfilter/ip_set.h246
-rw-r--r--kernel/include/linux/netfilter/ip_set_bitmap.h6
-rw-r--r--kernel/include/linux/netfilter/ip_set_chash.h1096
-rw-r--r--kernel/include/linux/netfilter/ip_set_getport.h25
-rw-r--r--kernel/include/linux/netfilter/ip_set_hash.h5
-rw-r--r--kernel/include/linux/netfilter/ip_set_jhash.h152
-rw-r--r--kernel/include/linux/netfilter/ip_set_kernel.h20
-rw-r--r--kernel/include/linux/netfilter/ip_set_list.h21
-rw-r--r--kernel/include/linux/netfilter/ip_set_slist.h86
-rw-r--r--kernel/include/linux/netfilter/ip_set_timeout.h35
-rw-r--r--kernel/include/linux/netfilter/ipt_set.h21
-rw-r--r--kernel/include/linux/netfilter/xt_set.h55
-rw-r--r--kernel/ip_set.c824
-rw-r--r--kernel/ip_set_bitmap_ip.c173
-rw-r--r--kernel/ip_set_bitmap_ipmac.c486
-rw-r--r--kernel/ip_set_bitmap_port.c141
-rw-r--r--kernel/ip_set_hash_ip.c539
-rw-r--r--kernel/ip_set_hash_ip_src.c473
-rw-r--r--kernel/ip_set_hash_ipport.c538
-rw-r--r--kernel/ip_set_hash_ipportip.c580
-rw-r--r--kernel/ip_set_hash_ipportnet.c696
-rw-r--r--kernel/ip_set_hash_net.c556
-rw-r--r--kernel/ip_set_iptreemap.c700
-rw-r--r--kernel/ip_set_list_set.c731
-rw-r--r--kernel/ip_set_tree_ip.c464
-rw-r--r--kernel/ipt_SET.c242
-rw-r--r--kernel/ipt_set.c238
-rw-r--r--kernel/xt_set.c356
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/PROTOCOL6
-rw-r--r--lib/data.c67
-rw-r--r--lib/mnl.c2
-rw-r--r--lib/parse.c449
-rw-r--r--lib/print.c27
-rw-r--r--lib/session.c98
-rw-r--r--lib/types.c58
-rw-r--r--lib/utils.c103
-rw-r--r--netlink.patch16
-rw-r--r--src/Makefile.am1
-rw-r--r--src/errcode.c26
-rw-r--r--src/ipset.81109
-rw-r--r--src/ipset.c194
-rw-r--r--src/ipset_bitmap_ip.c16
-rw-r--r--src/ipset_bitmap_ipmac.c14
-rw-r--r--src/ipset_bitmap_port.c12
-rw-r--r--src/ipset_hash_ip.c52
-rw-r--r--src/ipset_hash_ipport.c66
-rw-r--r--src/ipset_hash_ipportip.c66
-rw-r--r--src/ipset_hash_ipportnet.c83
-rw-r--r--src/ipset_hash_net.c59
-rw-r--r--src/ipset_iptreemap.c208
-rw-r--r--src/ipset_list_set.c31
-rw-r--r--src/ipset_tree_ip.c75
-rw-r--r--src/ui.c231
-rw-r--r--tests/Makefile450
-rw-r--r--tests/Makefile.am5
-rw-r--r--tests/Makefile.in450
-rw-r--r--tests/bitmap:ip.t16
-rw-r--r--tests/bitmap:ip.t.list03
-rw-r--r--tests/bitmap:ip.t.list13
-rw-r--r--tests/bitmap:ip.t.list23
-rw-r--r--tests/bitmap:ip.t.list33
-rw-r--r--tests/bitmap:ip.t.list413
-rw-r--r--tests/bitmap:ip.t.list59
-rw-r--r--tests/bitmap:ip.t.list6265
-rw-r--r--tests/hash:ip,port,ip.t51
-rw-r--r--tests/hash:ip,port,ip.t.list011
-rw-r--r--tests/hash:ip,port,ip.t.list17
-rw-r--r--tests/hash:ip,port.t43
-rw-r--r--tests/hash:ip,port.t.list011
-rw-r--r--tests/hash:ip,port.t.list17
-rw-r--r--tests/hash:ip.t14
-rw-r--r--tests/hash:ip.t.list05
-rw-r--r--tests/hash:ip.t.list15
-rw-r--r--tests/hash:ip.t.list29
-rw-r--r--tests/hash:ip.t.list310
-rw-r--r--tests/hash:ip6,port,ip6.t51
-rw-r--r--tests/hash:ip6,port,ip6.t.list011
-rw-r--r--tests/hash:ip6,port,ip6.t.list17
-rw-r--r--tests/hash:ip6,port,net6.t45
-rw-r--r--tests/hash:ip6,port,net6.t.list011
-rw-r--r--tests/hash:ip6,port.t43
-rw-r--r--tests/hash:ip6,port.t.list011
-rw-r--r--tests/hash:ip6,port.t.list17
-rw-r--r--tests/hash:ip6.t77
-rw-r--r--tests/hash:ip6.t.list08
-rw-r--r--tests/hash:ip6.t.list18
-rw-r--r--tests/hash:ip6.t.list29
-rw-r--r--tests/hash:ip6.t.list310
-rw-r--r--tests/hash:net.t49
-rw-r--r--tests/hash:net.t.list011
-rw-r--r--tests/hash:net.t.list17
-rw-r--r--tests/hash:net6.t49
-rw-r--r--tests/hash:net6.t.list011
-rw-r--r--tests/hash:net6.t.list17
-rw-r--r--tests/iphash.t10
-rw-r--r--tests/iphash.t.list05
-rw-r--r--tests/iphash.t.list15
-rw-r--r--tests/ipmap.t2
-rw-r--r--tests/ipmap.t.list03
-rw-r--r--tests/ipmap.t.list13
-rw-r--r--tests/ipmap.t.list23
-rw-r--r--tests/ipmap.t.list33
-rw-r--r--tests/ipmap.t.list43
-rw-r--r--tests/ipporthash.t44
-rw-r--r--tests/ipporthash.t.list08
-rw-r--r--tests/ipporthash.t.list18
-rw-r--r--tests/ipportiphash.t40
-rw-r--r--tests/ipportiphash.t.list08
-rw-r--r--tests/ipportiphash.t.list16
-rw-r--r--tests/ipportnethash.t26
-rw-r--r--tests/ipportnethash.t.list08
-rw-r--r--tests/ipportnethash.t.list18
-rw-r--r--tests/iptree.t2
-rw-r--r--tests/iptree.t.list07
-rw-r--r--tests/iptreemap.t4
-rw-r--r--tests/iptreemap.t.list035
-rw-r--r--tests/macipmap.t12
-rw-r--r--tests/macipmap.t.list03
-rw-r--r--tests/macipmap.t.list13
-rw-r--r--tests/macipmap.t.list23
-rw-r--r--tests/macipmap.t.list310
-rw-r--r--tests/nethash.t30
-rw-r--r--tests/nethash.t.list08
-rw-r--r--tests/portmap.t6
-rw-r--r--tests/portmap.t.list03
-rw-r--r--tests/portmap.t.list13
-rw-r--r--tests/portmap.t.list39
-rwxr-xr-xtests/runtest.sh19
-rw-r--r--tests/setlist.t6
-rw-r--r--tests/setlist.t.list05
-rwxr-xr-xtests/sort.sh4
145 files changed, 9165 insertions, 5846 deletions
diff --git a/Make_global.am b/Make_global.am
index 5c65103..ac9a2ca 100644
--- a/Make_global.am
+++ b/Make_global.am
@@ -39,7 +39,7 @@ AM_CFLAGS = -std=gnu99 \
-Wwrite-strings \
-Wno-missing-field-initializers \
-Werror \
- -g -ggdb
+ -g -ggdb -gdwarf-2 -g3
endif
if ! ENABLE_VERBOSE
diff --git a/Makefile.am b/Makefile.am
index 2eb7a00..e5c1c83 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,6 +33,7 @@ update_includes:
./update ip_set.h
./update ip_set_bitmap.h
./update ip_set_hash.h
+ ./update ip_set_list.h
tests:
cd tests; ./runtest.sh
diff --git a/include/libipset/data.h b/include/libipset/data.h
index 0ebc1eb..936b807 100644
--- a/include/libipset/data.h
+++ b/include/libipset/data.h
@@ -50,7 +50,8 @@ enum ipset_opt {
IPSET_OPT_EXIST,
IPSET_OPT_BEFORE,
/* Internal options */
- IPSET_OPT_FLAGS = 48,
+ IPSET_OPT_FLAGS = 48, /* IPSET_FLAG_EXIST| */
+ IPSET_OPT_CADT_FLAGS, /* IPSET_FLAG_BEFORE| */
IPSET_OPT_ELEM,
IPSET_OPT_TYPE,
IPSET_OPT_LINENO,
@@ -63,7 +64,10 @@ enum ipset_opt {
#define IPSET_FLAGS_ALL (~0LL)
#define IPSET_CREATE_FLAGS \
- ( IPSET_FLAG(IPSET_OPT_IP) \
+ ( IPSET_FLAG(IPSET_OPT_FAMILY) \
+ | IPSET_FLAG(IPSET_OPT_TYPENAME)\
+ | IPSET_FLAG(IPSET_OPT_TYPE) \
+ | IPSET_FLAG(IPSET_OPT_IP) \
| IPSET_FLAG(IPSET_OPT_IP_TO) \
| IPSET_FLAG(IPSET_OPT_CIDR) \
| IPSET_FLAG(IPSET_OPT_PORT) \
@@ -89,14 +93,17 @@ enum ipset_opt {
| IPSET_FLAG(IPSET_OPT_NAMEREF) \
| IPSET_FLAG(IPSET_OPT_IP2) \
| IPSET_FLAG(IPSET_OPT_CIDR2) \
+ | IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\
| IPSET_FLAG(IPSET_OPT_BEFORE))
struct ipset_data;
+extern void ipset_strncpy(char *dst, const char *src, size_t len);
extern bool ipset_data_flags_test(const struct ipset_data *data,
uint64_t flags);
extern void ipset_data_flags_set(struct ipset_data *data, uint64_t flags);
extern void ipset_data_flags_unset(struct ipset_data *data, uint64_t flags);
+extern bool ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt);
extern int ipset_data_set(struct ipset_data *data, enum ipset_opt opt,
const void *value);
diff --git a/include/libipset/debug.h b/include/libipset/debug.h
new file mode 100644
index 0000000..1e32ce2
--- /dev/null
+++ b/include/libipset/debug.h
@@ -0,0 +1,35 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef LIBIPSET_DEBUG_H
+#define LIBIPSET_DEBUG_H
+
+#define IPSET_DEBUG
+
+#ifdef IPSET_DEBUG
+#include <stdio.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#define D(fmt, args...) \
+ fprintf(stderr, "%s: %s: " fmt "\n", __FILE__, __FUNCTION__ , ## args)
+#define IF_D(test, fmt, args...) \
+ if (test) \
+ D(fmt , ## args)
+
+static inline void
+dump_nla(struct nlattr *nla[], int maxlen)
+{
+ int i;
+ for (i = 0; i < maxlen; i++)
+ D("nla[%u] does%s exist", i, nla[i] ? "" : " NOT");
+}
+#else
+#define D(fmt, args...)
+#define IF_D(test, fmt, args...)
+#define dump_nla(nla, maxlen)
+#endif
+
+#endif /* LIBIPSET_DEBUG_H */
diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
index 254fb21..4af75ba 100644
--- a/include/libipset/linux_ip_set.h
+++ b/include/libipset/linux_ip_set.h
@@ -11,14 +11,10 @@
* published by the Free Software Foundation.
*/
-#if 1
-#define IP_SET_DEBUG
-#endif
-
/* The protocol version */
#define IPSET_PROTOCOL 5
-/* The max length of strings: set and type identifiers */
+/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
/* Message types and commands */
@@ -43,6 +39,7 @@ enum ipset_cmd {
IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */
IPSET_CMD_HELP, /* Get help */
IPSET_CMD_VERSION, /* Get program version */
+ IPSET_CMD_QUIT, /* Quit from interactive mode */
IPSET_CMD_MAX,
@@ -58,6 +55,7 @@ enum {
IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */
IPSET_ATTR_REVISION, /* Settype revision */
IPSET_ATTR_FAMILY, /* Settype family */
+ IPSET_ATTR_FLAGS, /* Flags at command level */
IPSET_ATTR_DATA, /* Nested attributes */
IPSET_ATTR_ADT, /* Multiple data containers */
IPSET_ATTR_LINENO, /* Restore lineno */
@@ -77,8 +75,8 @@ enum {
IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
IPSET_ATTR_PORT_TO,
IPSET_ATTR_TIMEOUT,
- IPSET_ATTR_FLAGS,
- /* IPSET_ATTR_LINENO */
+ IPSET_ATTR_CADT_FLAGS,
+ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
@@ -123,15 +121,19 @@ enum ipset_errno {
IPSET_ERR_INVALID_NETMASK,
IPSET_ERR_INVALID_FAMILY,
IPSET_ERR_TIMEOUT,
+ IPSET_ERR_REFERENCED,
+ /* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 160,
};
-
-enum ipset_data_flags {
+
+enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
-
- IPSET_FLAG_BIT_BEFORE = 2,
+};
+
+enum ipset_cadt_flags {
+ IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE),
};
@@ -140,32 +142,9 @@ enum ipset_adt {
IPSET_ADD,
IPSET_DEL,
IPSET_TEST,
- IPSET_CREATE,
+ IPSET_ADT_MAX,
+ IPSET_CREATE = IPSET_ADT_MAX,
IPSET_CADT_MAX,
};
-#ifndef __KERNEL__
-#ifdef IP_SET_DEBUG
-#include <stdio.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#define D(format, args...) do { \
- fprintf(stderr, "%s: %s: ", __FILE__, __FUNCTION__); \
- fprintf(stderr, format "\n" , ## args); \
-} while (0)
-static inline void
-dump_nla(struct nlattr *nla[], int maxlen)
-{
- int i;
-
- for (i = 0; i < maxlen; i++)
- D("nla[%u] does%s exist", i, !nla[i] ? " NOT" : "");
-}
-
-#else
-#define D(format, args...)
-#define dump_nla(nla, maxlen)
-#endif
-#endif /* !__KERNEL__ */
-
#endif /* __IP_SET_H */
diff --git a/include/libipset/linux_ip_set_list.h b/include/libipset/linux_ip_set_list.h
new file mode 100644
index 0000000..cf282c5
--- /dev/null
+++ b/include/libipset/linux_ip_set_list.h
@@ -0,0 +1,14 @@
+#ifndef __IP_SET_LIST_H
+#define __IP_SET_LIST_H
+
+/* List type specific error codes */
+enum {
+ IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC,
+ IPSET_ERR_LOOP,
+ IPSET_ERR_BEFORE,
+ IPSET_ERR_NAMEREF,
+ IPSET_ERR_LIST_FULL,
+ IPSET_ERR_REF_EXIST,
+};
+
+#endif /* __IP_SET_LIST_H */
diff --git a/include/libipset/parse.h b/include/libipset/parse.h
index 09c1db4..143e2b3 100644
--- a/include/libipset/parse.h
+++ b/include/libipset/parse.h
@@ -17,6 +17,9 @@
struct ipset_session;
+typedef int (*ipset_parsefn)(struct ipset_session *s,
+ enum ipset_opt opt, const char *str);
+
extern int ipset_parse_ether(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_single_port(struct ipset_session *session,
@@ -24,7 +27,7 @@ extern int ipset_parse_single_port(struct ipset_session *session,
extern int ipset_parse_port(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_family(struct ipset_session *session,
- int opt, const char *str);
+ enum ipset_opt opt, const char *str);
extern int ipset_parse_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_single_ip(struct ipset_session *session,
@@ -35,8 +38,16 @@ extern int ipset_parse_range(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_netrange(struct ipset_session *session,
enum ipset_opt opt, const char *str);
+extern int ipset_parse_iprange(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
+extern int ipset_parse_ipnet(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
extern int ipset_parse_name(struct ipset_session *session,
enum ipset_opt opt, const char *str);
+extern int ipset_parse_before(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
+extern int ipset_parse_after(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
extern int ipset_parse_setname(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_uint32(struct ipset_session *session,
@@ -51,7 +62,18 @@ extern int ipset_parse_typename(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_output(struct ipset_session *session,
int opt, const char *str);
+extern int ipset_parse_ignored(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
extern int ipset_parse_elem(struct ipset_session *session,
enum ipset_opt opt, const char *str);
+extern int ipset_call_parser(struct ipset_session *session,
+ ipset_parsefn parse, const char *optstr,
+ enum ipset_opt optional, const char *str);
+
+/* Compatibility parser functions */
+extern int ipset_parse_iptimeout(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
+extern int ipset_parse_name_compat(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
#endif /* LIBIPSET_PARSE_H */
diff --git a/include/libipset/print.h b/include/libipset/print.h
index 343386b..dbb70f3 100644
--- a/include/libipset/print.h
+++ b/include/libipset/print.h
@@ -9,11 +9,15 @@
#include <libipset/data.h> /* enum ipset_opt */
+typedef int (*ipset_printfn)(char *buf, unsigned int len,
+ const struct ipset_data *data, enum ipset_opt opt,
+ uint8_t env);
+
extern int ipset_print_ether(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env);
extern int ipset_print_family(char *buf, unsigned int len,
- const struct ipset_data *data, int opt,
+ const struct ipset_data *data, enum ipset_opt opt,
uint8_t env);
extern int ipset_print_type(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
diff --git a/include/libipset/session.h b/include/libipset/session.h
index 71b8e02..cc0940e 100644
--- a/include/libipset/session.h
+++ b/include/libipset/session.h
@@ -12,7 +12,6 @@
#include <stdio.h> /* printf */
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
-#include <libipset/ui.h> /* enum ipset_envopt */
/* Report and output buffer sizes */
#define IPSET_ERRORBUFLEN 1024
@@ -54,6 +53,23 @@ extern const char * ipset_session_warning(const struct ipset_session *session);
#define ipset_session_data_get(session, opt) \
ipset_data_get(ipset_session_data(session), opt)
+/* Environment option flags */
+enum ipset_envopt {
+ IPSET_ENV_BIT_SORTED = 0,
+ IPSET_ENV_SORTED = (1 << IPSET_ENV_BIT_SORTED),
+ IPSET_ENV_BIT_QUIET = 1,
+ IPSET_ENV_QUIET = (1 << IPSET_ENV_BIT_QUIET),
+ IPSET_ENV_BIT_RESOLVE = 2,
+ IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE),
+ IPSET_ENV_BIT_EXIST = 3,
+ IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST),
+};
+
+extern int ipset_envopt_parse(struct ipset_session *session,
+ int env, const char *str);
+extern bool ipset_envopt_test(struct ipset_session *session,
+ enum ipset_envopt env);
+
enum ipset_output_mode {
IPSET_LIST_NONE,
IPSET_LIST_PLAIN,
@@ -61,10 +77,6 @@ enum ipset_output_mode {
IPSET_LIST_XML,
};
-extern int ipset_envopt_parse(struct ipset_session *session,
- int env, const char *str);
-extern bool ipset_envopt_test(struct ipset_session *session,
- enum ipset_envopt env);
extern int ipset_session_output(struct ipset_session *session,
enum ipset_output_mode mode);
diff --git a/include/libipset/types.h b/include/libipset/types.h
index 461931a..45d5e3d 100644
--- a/include/libipset/types.h
+++ b/include/libipset/types.h
@@ -10,6 +10,8 @@
#include <stdint.h> /* uintxx_t */
#include <libipset/data.h> /* enum ipset_opt */
+#include <libipset/parse.h> /* ipset_parsefn */
+#include <libipset/print.h> /* ipset_printfn */
#include <libipset/linux_ip_set.h> /* IPSET_MAXNAMELEN */
#define AF_INET46 255
@@ -39,15 +41,9 @@ enum {
struct ipset_session;
-typedef int (*ipset_parsefn)(struct ipset_session *s,
- enum ipset_opt opt, const char *str);
-typedef int (*ipset_printfn)(char *buf, unsigned int len,
- const struct ipset_data *data, enum ipset_opt opt,
- uint8_t env);
-
/* Parse and print type-specific arguments */
struct ipset_arg {
- const char *name[3]; /* option names */
+ const char *name[2]; /* option names */
int has_arg; /* mandatory/optional/no arg */
enum ipset_opt opt; /* argumentum type */
ipset_parsefn parse; /* parser function */
@@ -81,13 +77,13 @@ struct ipset_elem {
*/
struct ipset_type {
char name[IPSET_MAXNAMELEN]; /* type name */
- char alias[IPSET_MAXNAMELEN]; /* name alias */
uint8_t revision; /* revision number */
uint8_t family; /* supported family */
uint8_t dimension; /* elem dimension */
int8_t kernel_check; /* kernel check */
bool last_elem_optional; /* last element optional */
struct ipset_elem elem[IPSET_DIM_MAX]; /* parse elem */
+ ipset_parsefn compat_parse_elem; /* compatibility parser */
const struct ipset_arg *args[IPSET_CADT_MAX]; /* create/ADT args except elem */
uint64_t mandatory[IPSET_CADT_MAX]; /* create/ADT mandatory flags */
uint64_t full[IPSET_CADT_MAX]; /* full args flags */
@@ -95,13 +91,18 @@ struct ipset_type {
const char *usage; /* terse usage */
struct ipset_type *next;
+ const char *alias[]; /* name alias(es) */
};
-extern int ipset_cache_add(const char *name, const struct ipset_type *type);
+extern int ipset_cache_add(const char *name, const struct ipset_type *type,
+ uint8_t family);
extern int ipset_cache_del(const char *name);
extern int ipset_cache_rename(const char *from, const char *to);
extern int ipset_cache_swap(const char *from, const char *to);
+extern int ipset_cache_init(void);
+extern void ipset_cache_fini(void);
+
extern const struct ipset_type * ipset_type_get(struct ipset_session *session,
enum ipset_cmd cmd);
extern const struct ipset_type * ipset_type_check(struct ipset_session *session);
@@ -109,20 +110,7 @@ extern const struct ipset_type * ipset_type_check(struct ipset_session *session)
extern int ipset_type_add(struct ipset_type *type);
extern const struct ipset_type * ipset_types(void);
extern const char * ipset_typename_resolve(const char *str);
-
-extern int ipset_types_init(void);
-extern void ipset_types_fini(void);
-
-/* The known set types: (typename, revision, family) is unique */
-extern struct ipset_type ipset_bitmap_ip0;
-extern struct ipset_type ipset_bitmap_ipmac0;
-extern struct ipset_type ipset_bitmap_port0;
-extern struct ipset_type ipset_hash_ip0;
-extern struct ipset_type ipset_hash_net0;
-extern struct ipset_type ipset_hash_ipport0;
-extern struct ipset_type ipset_hash_ipportip0;
-extern struct ipset_type ipset_hash_ipportnet0;
-extern struct ipset_type ipset_tree_ip0;
-extern struct ipset_type ipset_list_set0;
+extern bool ipset_match_typename(const char *str,
+ const struct ipset_type *t);
#endif /* LIBIPSET_TYPES_H */
diff --git a/include/libipset/ui.h b/include/libipset/ui.h
index 044e586..f8eeae0 100644
--- a/include/libipset/ui.h
+++ b/include/libipset/ui.h
@@ -9,25 +9,14 @@
/* Commands in userspace */
struct ipset_commands {
- const char *name[6];
- const char *help;
+ enum ipset_cmd cmd;
int has_arg;
+ const char *name[2];
+ const char *help;
};
extern const struct ipset_commands ipset_commands[];
-/* Environment option flags */
-enum ipset_envopt {
- IPSET_ENV_BIT_SORTED = 0,
- IPSET_ENV_SORTED = (1 << IPSET_ENV_BIT_SORTED),
- IPSET_ENV_BIT_QUIET = 1,
- IPSET_ENV_QUIET = (1 << IPSET_ENV_BIT_QUIET),
- IPSET_ENV_BIT_RESOLVE = 2,
- IPSET_ENV_RESOLVE = (1 << IPSET_ENV_BIT_RESOLVE),
- IPSET_ENV_BIT_EXIST = 3,
- IPSET_ENV_EXIST = (1 << IPSET_ENV_BIT_EXIST),
-};
-
struct ipset_session;
struct ipset_data;
@@ -35,7 +24,7 @@ struct ipset_data;
struct ipset_envopts {
int flag;
int has_arg;
- const char *name[3];
+ const char *name[2];
const char *help;
int (*parse)(struct ipset_session *s, int flag, const char *str);
int (*print)(char *buf, unsigned int len,
@@ -44,4 +33,9 @@ struct ipset_envopts {
extern const struct ipset_envopts ipset_envopts[];
+extern bool ipset_match_cmd(const char *arg, const char * const name[]);
+extern bool ipset_match_option(const char *arg, const char * const name[]);
+extern bool ipset_match_envopt(const char *arg, const char * const name[]);
+extern void ipset_shift_argv(int *argc, char *argv[], int from);
+
#endif /* LIBIPSET_UI_H */
diff --git a/include/libipset/utils.h b/include/libipset/utils.h
index 2d12e91..672bfa9 100644
--- a/include/libipset/utils.h
+++ b/include/libipset/utils.h
@@ -7,7 +7,6 @@
#ifndef LIBIPSET_UTILS_H
#define LIBIPSET_UTILS_H
-#include <stdbool.h> /* bool */
#include <string.h> /* strcmp */
#include <netinet/in.h> /* struct in[6]_addr */
@@ -37,9 +36,4 @@ in6cpy(struct in6_addr *dest, const struct in6_addr *src)
memcpy(dest, src, sizeof(struct in6_addr));
}
-extern char * ipset_strchr(const char *str, const char *sep);
-extern bool ipset_name_match(const char *arg, const char * const name[]);
-extern void ipset_shift_argv(int *argc, char *argv[], int from);
-extern void ipset_strncpy(char *dst, const char *src, size_t len);
-
#endif /* LIBIPSET_UTILS_H */
diff --git a/kernel/Kbuild b/kernel/Kbuild
index c171711..9c7771a 100644
--- a/kernel/Kbuild
+++ b/kernel/Kbuild
@@ -1,14 +1,11 @@
EXTRA_CFLAGS := -I$(M)/include \
-DCONFIG_IP_SET_MAX=$(IP_SET_MAX)
-obj-m += ip_set.o
-#ipt_set.o ipt_SET.o
+obj-m += ip_set.o xt_set.o
obj-m += ip_set_bitmap_ip.o ip_set_bitmap_ipmac.o ip_set_bitmap_port.o
-obj-m += ip_set_hash_ip.o
-#obj-m += ip_set_iphash.o ip_set_nethash.o ip_set_ipporthash.o
-#obj-m += ip_set_ipportiphash.o ip_set_ipportnethash.o
-#obj-m += ip_set_iptree.o ip_set_iptreemap.o
-#obj-m += ip_set_setlist.o
+obj-m += ip_set_hash_ip.o ip_set_hash_ipport.o ip_set_hash_ipportip.o
+obj-m += ip_set_hash_net.o ip_set_hash_ipportnet.o
+obj-m += ip_set_list_set.o
# It's for me...
incdirs := $(M) $(M)/include/linux/netfilter
diff --git a/kernel/include/linux/netfilter/ip_set.h b/kernel/include/linux/netfilter/ip_set.h
index d0b47a0..e700503 100644
--- a/kernel/include/linux/netfilter/ip_set.h
+++ b/kernel/include/linux/netfilter/ip_set.h
@@ -11,14 +11,10 @@
* published by the Free Software Foundation.
*/
-#if 1
-#define IP_SET_DEBUG
-#endif
-
/* The protocol version */
#define IPSET_PROTOCOL 5
-/* The max length of strings: set and type identifiers */
+/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
/* Message types and commands */
@@ -43,6 +39,7 @@ enum ipset_cmd {
IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */
IPSET_CMD_HELP, /* Get help */
IPSET_CMD_VERSION, /* Get program version */
+ IPSET_CMD_QUIT, /* Quit from interactive mode */
IPSET_CMD_MAX,
@@ -58,6 +55,7 @@ enum {
IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */
IPSET_ATTR_REVISION, /* Settype revision */
IPSET_ATTR_FAMILY, /* Settype family */
+ IPSET_ATTR_FLAGS, /* Flags at command level */
IPSET_ATTR_DATA, /* Nested attributes */
IPSET_ATTR_ADT, /* Multiple data containers */
IPSET_ATTR_LINENO, /* Restore lineno */
@@ -77,8 +75,8 @@ enum {
IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
IPSET_ATTR_PORT_TO,
IPSET_ATTR_TIMEOUT,
- IPSET_ATTR_FLAGS,
- /* IPSET_ATTR_LINENO */
+ IPSET_ATTR_CADT_FLAGS,
+ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
@@ -123,15 +121,19 @@ enum ipset_errno {
IPSET_ERR_INVALID_NETMASK,
IPSET_ERR_INVALID_FAMILY,
IPSET_ERR_TIMEOUT,
+ IPSET_ERR_REFERENCED,
+ /* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 160,
};
-
-enum ipset_data_flags {
+
+enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
-
- IPSET_FLAG_BIT_BEFORE = 2,
+};
+
+enum ipset_cadt_flags {
+ IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE),
};
@@ -140,35 +142,13 @@ enum ipset_adt {
IPSET_ADD,
IPSET_DEL,
IPSET_TEST,
- IPSET_CREATE,
+ IPSET_ADT_MAX,
+ IPSET_CREATE = IPSET_ADT_MAX,
IPSET_CADT_MAX,
};
-#ifndef __KERNEL__
-#ifdef IP_SET_DEBUG
-#include <stdio.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#define D(format, args...) do { \
- fprintf(stderr, "%s: %s: ", __FILE__, __FUNCTION__); \
- fprintf(stderr, format "\n" , ## args); \
-} while (0)
-static inline void
-dump_nla(struct nlattr *nla[], int maxlen)
-{
- int i;
-
- for (i = 0; i < maxlen; i++)
- D("nla[%u] does%s exist", i, !nla[i] ? " NOT" : "");
-}
-
-#else
-#define D(format, args...)
-#define dump_nla(nla, maxlen)
-#endif
-#endif /* !__KERNEL__ */
-
#ifdef __KERNEL__
+#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/netlink.h>
#include <net/netlink.h>
@@ -176,19 +156,27 @@ dump_nla(struct nlattr *nla[], int maxlen)
/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t
* and IPSET_INVALID_ID if you want to increase the max number of sets.
*/
-typedef uint16_t ip_set_id_t;
+typedef u16 ip_set_id_t;
#define IPSET_INVALID_ID 65535
+enum ip_set_dim {
+ IPSET_DIM_ZERO = 0,
+ IPSET_DIM_ONE,
+ IPSET_DIM_TWO,
+ IPSET_DIM_THREE,
+ /* Max dimension in elements.
+ * If changed, new revision of iptables match/target is required.
+ */
+ IPSET_DIM_MAX = 6,
+};
+
/* Option flags for kernel operations */
enum ip_set_kopt {
- /* Bit 0 is reserved */
- IPSET_SRC_FLAG = 1,
- IPSET_SRC = (1 << IPSET_SRC_FLAG),
- IPSET_DST_FLAG = 2,
- IPSET_DST = (1 << IPSET_DST_FLAG),
- IPSET_INV_FLAG = 3,
- IPSET_INV = (1 << IPSET_INV_FLAG),
+ IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO),
+ IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE),
+ IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO),
+ IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE),
};
/* Set features */
@@ -203,72 +191,60 @@ enum ip_set_feature {
IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG),
IPSET_TYPE_NAME_FLAG = 4,
IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG),
+ /* Actually just a flag for dumping */
+ IPSET_DUMP_LAST_FLAG = 7,
+ IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG),
};
+/* Calculate the bytes required to store the inclusive range of a-b */
static inline int
-bitmap_bytes(uint32_t a, uint32_t b)
+bitmap_bytes(u32 a, u32 b)
{
return 4 * ((((b - a + 8) / 8) + 3) / 4);
}
-#define ip_set_printk(format, args...) \
- do { \
- printk("%s: %s: ", __FILE__, __FUNCTION__); \
- printk(format "\n" , ## args); \
- } while (0)
-
-#if defined(IP_SET_DEBUG)
-#define D(format, args...) \
- do { \
- printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\
- printk(format "\n" , ## args); \
- } while (0)
-
-static inline void
-dump_nla(const struct nlattr * const nla[], int maxlen)
-{
- int i;
-
- for (i = 0; i < maxlen; i++)
- printk("nlattr[%u] does%s exist\n", i, nla[i] ? "" : " NOT");
-}
-#else
-#define D(format, args...)
-#define dump_nla(nla, maxlen)
-#endif
-
struct ip_set;
+typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout);
+
/* Set type, variant-specific part */
struct ip_set_type_variant {
/* Kernelspace: test/add/del entries */
int (*kadt)(struct ip_set *set, const struct sk_buff * skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags);
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
/* Userspace: test/add/del entries */
int (*uadt)(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags);
+ enum ipset_adt adt, u32 *lineno, u32 flags);
+
+ /* Low level add/del/test entries */
+ ipset_adtfn adt[IPSET_ADT_MAX];
/* When adding entries and set is full, try to resize the set */
- int (*resize)(struct ip_set *set, uint8_t retried);
+ int (*resize)(struct ip_set *set, gfp_t gfp_flags, bool retried);
/* Destroy the set */
void (*destroy)(struct ip_set *set);
/* Flush the elements */
void (*flush)(struct ip_set *set);
-
+ /* Expire entries before listing */
+ void (*expire)(struct ip_set *set);
/* List set header data */
int (*head)(struct ip_set *set, struct sk_buff *skb);
/* List elements */
int (*list)(struct ip_set *set, struct sk_buff *skb,
struct netlink_callback *cb);
+
+ /* Return true if "b" set is the same as "a"
+ * according to the set parameters */
+ bool (*same_set)(const struct ip_set *a, const struct ip_set *b);
};
/* Flags for the set type variants */
enum ip_set_type_flags {
- IP_SET_FLAG_VMALLOC_BIT = 0,
- IP_SET_FLAG_VMALLOC = (1 << IP_SET_FLAG_VMALLOC_BIT),
- IP_SET_FLAG_TIMEOUT_BIT = 1,
- IP_SET_FLAG_TIMEOUT = (1 << IP_SET_FLAG_TIMEOUT_BIT),
+ /* Set members created by kmalloc */
+ IP_SET_FLAG_KMALLOC_BIT = 0,
+ IP_SET_FLAG_KMALLOC = (1 << IP_SET_FLAG_KMALLOC_BIT),
};
/* The core set type structure */
@@ -278,17 +254,19 @@ struct ip_set_type {
/* Typename */
char name[IPSET_MAXNAMELEN];
/* Protocol version */
- uint8_t protocol;
+ u8 protocol;
/* Set features to control swapping */
- uint8_t features;
+ u8 features;
+ /* Set type dimension */
+ u8 dimension;
/* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */
- uint8_t family;
+ u8 family;
/* Type revision */
- uint8_t revision;
+ u8 revision;
/* Create set */
int (*create)(struct ip_set *set,
- struct nlattr *head, int len, uint32_t flags);
+ struct nlattr *head, int len, u32 flags);
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me;
@@ -310,86 +288,90 @@ struct ip_set {
/* The type variant doing the real job */
const struct ip_set_type_variant *variant;
/* The actual INET family */
- uint8_t family;
+ u8 family;
/* Set type flags, filled/modified by create/resize */
- uint8_t flags;
+ u8 flags;
/* The type specific data */
void *data;
};
/* register and unregister set references */
-extern ip_set_id_t ip_set_get_byname(const char name[IPSET_MAXNAMELEN]);
+extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set);
extern void ip_set_put_byindex(ip_set_id_t index);
+extern const char * ip_set_name_byindex(ip_set_id_t index);
+extern ip_set_id_t ip_set_nfnl_get(const char *name);
+extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index);
+extern void ip_set_nfnl_put(ip_set_id_t index);
/* API for iptables set match, and SET target */
extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags);
+ u8 family, u8 dim, u8 flags);
extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags);
+ u8 family, u8 dim, u8 flags);
extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags);
+ u8 family, u8 dim, u8 flags);
/* Allocate members */
static inline void *
-ip_set_alloc(size_t size, gfp_t gfp_mask, uint8_t *flags)
+ip_set_alloc(size_t size, gfp_t gfp_mask, u8 *flags)
{
- void *members = kzalloc(size, gfp_mask);
+ void *members = kzalloc(size, gfp_mask | __GFP_NOWARN);
if (members) {
- *flags &= ~IP_SET_FLAG_VMALLOC;
- D("allocated with kmalloc %p", members);
+ *flags |= IP_SET_FLAG_KMALLOC;
+ pr_debug("%p: allocated with kmalloc", members);
return members;
}
members = __vmalloc(size, gfp_mask | __GFP_ZERO, PAGE_KERNEL);
if (!members)
return NULL;
- *flags |= IP_SET_FLAG_VMALLOC;
- D("allocated with vmalloc %p", members);
+ *flags &= ~IP_SET_FLAG_KMALLOC;
+ pr_debug("%p: allocated with vmalloc", members);
return members;
}
static inline void
-ip_set_free(void *members, uint8_t flags)
+ip_set_free(void *members, u8 flags)
{
- D("free with %s %p", flags & IP_SET_FLAG_VMALLOC ? "vmalloc" : "kmalloc",
- members);
- if (flags & IP_SET_FLAG_VMALLOC)
- vfree(members);
- else
+ pr_debug("%p: free with %s", members,
+ flags & IP_SET_FLAG_KMALLOC ? "kmalloc" : "vmalloc");
+ if (flags & IP_SET_FLAG_KMALLOC)
kfree(members);
+ else
+ vfree(members);
}
/* Useful converters */
-static inline uint32_t
+static inline u32
ip_set_get_h32(const struct nlattr *attr)
{
- uint32_t value = nla_get_u32(attr);
+ u32 value = nla_get_u32(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohl(value) : value;
}
-static inline uint16_t
+static inline u16
ip_set_get_h16(const struct nlattr *attr)
{
- uint16_t value = nla_get_u16(attr);
+ u16 value = nla_get_u16(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohs(value) : value;
}
-static inline uint32_t
+static inline u32
ip_set_get_n32(const struct nlattr *attr)
{
- uint32_t value = nla_get_u32(attr);
+ u32 value = nla_get_u32(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htonl(value);
}
-static inline uint16_t
+static inline u16
ip_set_get_n16(const struct nlattr *attr)
{
- uint16_t value = nla_get_u16(attr);
+ u16 value = nla_get_u16(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htons(value);
}
@@ -404,31 +386,49 @@ ip_set_get_n16(const struct nlattr *attr)
NLA_PUT_BE16(skb, type | NLA_F_NET_BYTEORDER, value)
/* Get address from skbuff */
-static inline uint32_t
-ip4addr(const struct sk_buff *skb, const uint8_t *flags)
+static inline u32
+ip4addr(const struct sk_buff *skb, bool src)
{
- return flags[0] & IPSET_SRC ? ip_hdr(skb)->saddr
- : ip_hdr(skb)->daddr;
+ return src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
}
static inline void
-ip4addrptr(const struct sk_buff *skb, const uint8_t *flags, uint32_t *addr)
+ip4addrptr(const struct sk_buff *skb, bool src, u32 *addr)
{
- *addr = flags[0] & IPSET_SRC ? ip_hdr(skb)->saddr
- : ip_hdr(skb)->daddr;
+ *addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
}
static inline void
-ip6addrptr(const struct sk_buff *skb, const uint8_t *flags,
- struct in6_addr *addr)
+ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr)
{
- memcpy(addr, flags[0] & IPSET_SRC ? &ipv6_hdr(skb)->saddr
- : &ipv6_hdr(skb)->daddr,
+ memcpy(addr, src ? &ipv6_hdr(skb)->saddr : &ipv6_hdr(skb)->daddr,
sizeof(*addr));
}
-#define pack_ip_port(map, ip, port) \
- (port + ((ip - ((map)->first_ip)) << 16))
+/* Interface to iptables/ip6tables */
+
+#define SO_IP_SET 83
+
+union ip_set_name_index {
+ char name[IPSET_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;
+};
#endif /* __KERNEL__ */
diff --git a/kernel/include/linux/netfilter/ip_set_bitmap.h b/kernel/include/linux/netfilter/ip_set_bitmap.h
index 49d0f5c..0d067d0 100644
--- a/kernel/include/linux/netfilter/ip_set_bitmap.h
+++ b/kernel/include/linux/netfilter/ip_set_bitmap.h
@@ -12,10 +12,10 @@ enum {
/* Common functions */
-static inline uint32_t
-range_to_mask(uint32_t from, uint32_t to, uint8_t *bits)
+static inline u32
+range_to_mask(u32 from, u32 to, u8 *bits)
{
- uint32_t mask = 0xFFFFFFFE;
+ u32 mask = 0xFFFFFFFE;
*bits = 32;
while (--(*bits) > 0 && mask && (to & mask) != from)
diff --git a/kernel/include/linux/netfilter/ip_set_chash.h b/kernel/include/linux/netfilter/ip_set_chash.h
new file mode 100644
index 0000000..0d77a5d
--- /dev/null
+++ b/kernel/include/linux/netfilter/ip_set_chash.h
@@ -0,0 +1,1096 @@
+#ifndef _IP_SET_CHASH_H
+#define _IP_SET_CHASH_H
+
+#include <linux/netfilter/ip_set_jhash.h>
+#include <linux/netfilter/ip_set_slist.h>
+#include <linux/netfilter/ip_set_timeout.h>
+
+#define CONCAT(a, b, c) a##b##c
+#define TOKEN(a, b, c) CONCAT(a, b, c)
+
+/* Cache friendly hash with resizing when linear searching becomes too long.
+ * Internally jhash is used with the assumption that the size of the stored
+ * data is a multiple of sizeof(u32). If storage supports timeout, the
+ * timeout field must be the last one in the data structure.
+ */
+
+/* Number of elements to store in an array block */
+#define CHASH_DEFAULT_ARRAY_SIZE 4
+/* Number of arrays: max ARRAY_SIZE * CHAIN_LIMIT "long" chains */
+#define CHASH_DEFAULT_CHAIN_LIMIT 3
+
+struct chash_nets {
+ u32 nets; /* number of elements per cidr */
+ u8 cidr; /* the cidr values added to the set */
+};
+
+struct chash {
+ struct slist *htable; /* Hashtable of single linked lists */
+ u32 maxelem; /* Max elements in the hash */
+ u32 elements; /* Current element (vs timeout) */
+ u32 initval; /* random jhash init value */
+ u32 timeout; /* timeout value, if enabled */
+ struct timer_list gc; /* garbage collection when timeout enabled */
+ u8 htable_bits; /* size of hash table == 2^htable_bits */
+ u8 array_size; /* number of elements in an array */
+ u8 chain_limit; /* max number of arrays */
+#ifdef IP_SET_HASH_WITH_NETMASK
+ u8 netmask; /* netmask value for subnets to store */
+#endif
+#ifdef IP_SET_HASH_WITH_NETS
+ struct chash_nets nets[0]; /* book keeping of networks */
+#endif
+};
+
+static inline u8
+htable_bits(u32 hashsize)
+{
+ /* Assume that hashsize == 2^htable_bits */
+ u8 bits = fls(hashsize - 1);
+ if (jhash_size(bits) != hashsize)
+ /* Round up to the first 2^n value */
+ bits = fls(hashsize);
+
+ return bits;
+}
+
+static inline void
+add_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr)
+{
+ u8 i;
+
+ pr_debug("add_cidr %u", cidr);
+ for (i = 0; i < host_mask - 1 && nets[i].cidr; i++) {
+ /* Add in increasing prefix order, so larger cidr first */
+ if (nets[i].cidr < cidr)
+ swap(nets[i].cidr, cidr);
+ }
+ if (i < host_mask - 1)
+ nets[i].cidr = cidr;
+}
+
+static inline void
+del_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr)
+{
+ u8 i;
+
+ pr_debug("del_cidr %u", cidr);
+ for (i = 0; i < host_mask - 2 && nets[i].cidr; i++) {
+ if (nets[i].cidr == cidr)
+ nets[i].cidr = cidr = nets[i+1].cidr;
+ }
+ nets[host_mask - 2].cidr = 0;
+}
+
+static void
+chash_destroy(struct slist *t, u8 htable_bits, u8 flags)
+{
+ struct slist *n, *tmp;
+ u32 i;
+
+ for (i = 0; i < jhash_size(htable_bits); i++)
+ slist_for_each_safe(n, tmp, &t[i])
+ /* FIXME: slab cache */
+ kfree(n);
+
+ ip_set_free(t, flags);
+}
+
+static size_t
+chash_memsize(const struct chash *h, size_t dsize, u8 host_mask)
+{
+ struct slist *n;
+ u32 i;
+ size_t memsize = sizeof(*h)
+#ifdef IP_SET_HASH_WITH_NETS
+ + sizeof(struct chash_nets) * (host_mask - 1)
+#endif
+ + jhash_size(h->htable_bits) * sizeof(struct slist);
+
+ for (i = 0; i < jhash_size(h->htable_bits); i++)
+ slist_for_each(n, &h->htable[i])
+ memsize += sizeof(struct slist)
+ + h->array_size * dsize;
+
+ return memsize;
+}
+
+static void
+ip_set_hash_flush(struct ip_set *set)
+{
+ struct chash *h = set->data;
+ struct slist *n, *tmp;
+ u32 i;
+
+ for (i = 0; i < jhash_size(h->htable_bits); i++) {
+ slist_for_each_safe(n, tmp, &h->htable[i])
+ /* FIXME: slab cache */
+ kfree(n);
+ h->htable[i].next = NULL;
+ }
+#ifdef IP_SET_HASH_WITH_NETS
+ memset(h->nets, 0, sizeof(struct chash_nets)
+ * (set->family == AF_INET ? 31 : 127));
+#endif
+ h->elements = 0;
+}
+
+static void
+ip_set_hash_destroy(struct ip_set *set)
+{
+ struct chash *h = set->data;
+
+ if (with_timeout(h->timeout))
+ del_timer_sync(&h->gc);
+
+ chash_destroy(h->htable, h->htable_bits, set->flags);
+ kfree(h);
+
+ set->data = NULL;
+}
+
+#define JHASH2(data, initval, htable_bits) \
+jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \
+ & jhash_mask(htable_bits)
+
+#endif /* _IP_SET_CHASH_H */
+
+/* Type/family dependent function prototypes */
+
+#define type_pf_data_equal TOKEN(TYPE, PF, _data_equal)
+#define type_pf_data_isnull TOKEN(TYPE, PF, _data_isnull)
+#define type_pf_data_copy TOKEN(TYPE, PF, _data_copy)
+#define type_pf_data_swap TOKEN(TYPE, PF, _data_swap)
+#define type_pf_data_zero_out TOKEN(TYPE, PF, _data_zero_out)
+#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask)
+#define type_pf_data_list TOKEN(TYPE, PF, _data_list)
+#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist)
+
+#define type_pf_elem TOKEN(TYPE, PF, _elem)
+#define type_pf_telem TOKEN(TYPE, PF, _telem)
+#define type_pf_data_timeout TOKEN(TYPE, PF, _data_timeout)
+#define type_pf_data_expired TOKEN(TYPE, PF, _data_expired)
+#define type_pf_data_swap_timeout TOKEN(TYPE, PF, _data_swap_timeout)
+#define type_pf_data_timeout_set TOKEN(TYPE, PF, _data_timeout_set)
+
+#define type_pf_chash_readd TOKEN(TYPE, PF, _chash_readd)
+#define type_pf_chash_del_elem TOKEN(TYPE, PF, _chash_del_elem)
+#define type_pf_chash_add TOKEN(TYPE, PF, _chash_add)
+#define type_pf_chash_del TOKEN(TYPE, PF, _chash_del)
+#define type_pf_chash_test_cidrs TOKEN(TYPE, PF, _chash_test_cidrs)
+#define type_pf_chash_test TOKEN(TYPE, PF, _chash_test)
+
+#define type_pf_chash_treadd TOKEN(TYPE, PF, _chash_treadd)
+#define type_pf_chash_del_telem TOKEN(TYPE, PF, _chash_del_telem)
+#define type_pf_chash_expire TOKEN(TYPE, PF, _chash_expire)
+#define type_pf_chash_tadd TOKEN(TYPE, PF, _chash_tadd)
+#define type_pf_chash_tdel TOKEN(TYPE, PF, _chash_tdel)
+#define type_pf_chash_ttest_cidrs TOKEN(TYPE, PF, _chash_ttest_cidrs)
+#define type_pf_chash_ttest TOKEN(TYPE, PF, _chash_ttest)
+
+#define type_pf_resize TOKEN(TYPE, PF, _resize)
+#define type_pf_tresize TOKEN(TYPE, PF, _tresize)
+#define type_pf_flush ip_set_hash_flush
+#define type_pf_destroy ip_set_hash_destroy
+#define type_pf_head TOKEN(TYPE, PF, _head)
+#define type_pf_list TOKEN(TYPE, PF, _list)
+#define type_pf_tlist TOKEN(TYPE, PF, _tlist)
+#define type_pf_same_set TOKEN(TYPE, PF, _same_set)
+#define type_pf_kadt TOKEN(TYPE, PF, _kadt)
+#define type_pf_uadt TOKEN(TYPE, PF, _uadt)
+#define type_pf_gc TOKEN(TYPE, PF, _gc)
+#define type_pf_gc_init TOKEN(TYPE, PF, _gc_init)
+#define type_pf_variant TOKEN(TYPE, PF, _variant)
+#define type_pf_tvariant TOKEN(TYPE, PF, _tvariant)
+
+/* Flavour without timeout */
+
+#define chash_data(n, i) \
+(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_elem))
+
+static int
+type_pf_chash_readd(struct chash *h, struct slist *t, u8 htable_bits,
+ const struct type_pf_elem *value, gfp_t gfp_flags)
+{
+ struct slist *n, *prev;
+ struct type_pf_elem *data;
+ void *tmp;
+ int i = 0, j = 0;
+ u32 hash = JHASH2(value, h->initval, htable_bits);
+
+ slist_for_each_prev(prev, n, &t[hash]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data)) {
+ tmp = n;
+ goto found;
+ }
+ }
+ j++;
+ }
+ if (j < h->chain_limit) {
+ tmp = kzalloc(h->array_size * sizeof(struct type_pf_elem)
+ + sizeof(struct slist), gfp_flags);
+ if (!tmp)
+ return -ENOMEM;
+ prev->next = (struct slist *) tmp;
+ data = chash_data(tmp, 0);
+ } else {
+ /* Rehashing */
+ return -EAGAIN;
+ }
+found:
+ type_pf_data_copy(data, value);
+ return 0;
+}
+
+static void
+type_pf_chash_del_elem(struct chash *h, struct slist *prev,
+ struct slist *n, int i)
+{
+ struct type_pf_elem *data = chash_data(n, i);
+ struct slist *tmp;
+ int j;
+
+ if (n->next != NULL) {
+ for (prev = n, tmp = n->next;
+ tmp->next != NULL;
+ prev = tmp, tmp = tmp->next)
+ /* Find last array */;
+ j = 0;
+ } else {
+ /* Already at last array */
+ tmp = n;
+ j = i;
+ }
+ /* Find last non-empty element */
+ for (; j < h->array_size - 1; j++)
+ if (type_pf_data_isnull(chash_data(tmp, j + 1)))
+ break;
+
+ if (!(tmp == n && i == j)) {
+ type_pf_data_swap(data, chash_data(tmp, j));
+ }
+#ifdef IP_SET_HASH_WITH_NETS
+ if (--h->nets[data->cidr-1].nets == 0)
+ del_cidr(h->nets, HOST_MASK, data->cidr);
+#endif
+ if (j == 0) {
+ prev->next = NULL;
+ kfree(tmp);
+ } else
+ type_pf_data_zero_out(chash_data(tmp, j));
+
+ h->elements--;
+}
+
+static int
+type_pf_resize(struct ip_set *set, gfp_t gfp_flags, bool retried)
+{
+ struct chash *h = set->data;
+ u8 htable_bits = h->htable_bits;
+ struct slist *t, *n;
+ const struct type_pf_elem *data;
+ u32 i, j;
+ u8 oflags, flags;
+ int ret;
+
+retry:
+ ret = 0;
+ htable_bits++;
+ if (!htable_bits)
+ /* In case we have plenty of memory :-) */
+ return -IPSET_ERR_HASH_FULL;
+ t = ip_set_alloc(jhash_size(htable_bits) * sizeof(struct slist),
+ gfp_flags, &flags);
+ if (!t)
+ return -ENOMEM;
+
+ write_lock_bh(&set->lock);
+ flags = oflags = set->flags;
+ for (i = 0; i < jhash_size(h->htable_bits); i++) {
+next_slot:
+ slist_for_each(n, &h->htable[i]) {
+ for (j = 0; j < h->array_size; j++) {
+ data = chash_data(n, j);
+ if (type_pf_data_isnull(data)) {
+ i++;
+ goto next_slot;
+ }
+ ret = type_pf_chash_readd(h, t, htable_bits,
+ data, gfp_flags);
+ if (ret < 0) {
+ write_unlock_bh(&set->lock);
+ chash_destroy(t, htable_bits, flags);
+ if (ret == -EAGAIN)
+ goto retry;
+ return ret;
+ }
+ }
+ }
+ }
+
+ n = h->htable;
+ i = h->htable_bits;
+
+ h->htable = t;
+ h->htable_bits = htable_bits;
+ set->flags = flags;
+ write_unlock_bh(&set->lock);
+
+ chash_destroy(n, i, oflags);
+
+ return 0;
+}
+
+static int
+type_pf_chash_add(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ const struct type_pf_elem *d = value;
+ struct slist *n, *prev, *t = h->htable;
+ struct type_pf_elem *data;
+ void *tmp;
+ int i = 0, j = 0;
+ u32 hash;
+
+#ifdef IP_SET_HASH_WITH_NETS
+ if (h->elements >= h->maxelem || h->nets[d->cidr-1].nets == UINT_MAX)
+#else
+ if (h->elements >= h->maxelem)
+#endif
+ return -IPSET_ERR_HASH_FULL;
+
+ hash = JHASH2(value, h->initval, h->htable_bits);
+ slist_for_each_prev(prev, n, &t[hash]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data)) {
+ tmp = n;
+ goto found;
+ }
+ if (type_pf_data_equal(data, d))
+ return -IPSET_ERR_EXIST;
+ }
+ j++;
+ }
+ if (j < h->chain_limit) {
+ tmp = kzalloc(h->array_size * sizeof(struct type_pf_elem)
+ + sizeof(struct slist), gfp_flags);
+ if (!tmp)
+ return -ENOMEM;
+ prev->next = (struct slist *) tmp;
+ data = chash_data(tmp, 0);
+ } else {
+ /* Rehashing */
+ return -EAGAIN;
+ }
+found:
+ type_pf_data_copy(data, d);
+#ifdef IP_SET_HASH_WITH_NETS
+ if (h->nets[d->cidr-1].nets++ == 0)
+ add_cidr(h->nets, HOST_MASK, d->cidr);
+#endif
+ h->elements++;
+ return 0;
+}
+
+static int
+type_pf_chash_del(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ const struct type_pf_elem *d = value;
+ struct slist *n, *prev;
+ int i;
+ struct type_pf_elem *data;
+ u32 hash = JHASH2(value, h->initval, h->htable_bits);
+
+ slist_for_each_prev(prev, n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data))
+ return -IPSET_ERR_EXIST;
+ if (type_pf_data_equal(data, d)) {
+ type_pf_chash_del_elem(h, prev, n, i);
+ return 0;
+ }
+ }
+
+ return -IPSET_ERR_EXIST;
+}
+
+#ifdef IP_SET_HASH_WITH_NETS
+static inline int
+type_pf_chash_test_cidrs(struct ip_set *set,
+ struct type_pf_elem *d,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ struct slist *n;
+ const struct type_pf_elem *data;
+ int i, j = 0;
+ u32 hash;
+ u8 host_mask = set->family == AF_INET ? 32 : 128;
+
+retry:
+ pr_debug("test by nets");
+ for (; j < host_mask - 1 && h->nets[j].cidr; j++) {
+ type_pf_data_netmask(d, h->nets[j].cidr);
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each(n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data)) {
+ j++;
+ goto retry;
+ }
+ if (type_pf_data_equal(data, d))
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+static inline int
+type_pf_chash_test(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ struct type_pf_elem *d = value;
+ struct slist *n;
+ const struct type_pf_elem *data;
+ int i;
+ u32 hash;
+#ifdef IP_SET_HASH_WITH_NETS
+ u8 host_mask = set->family == AF_INET ? 32 : 128;
+
+ if (d->cidr == host_mask)
+ return type_pf_chash_test_cidrs(set, d, gfp_flags, timeout);
+#endif
+
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each(n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data))
+ return 0;
+ if (type_pf_data_equal(data, d))
+ return 1;
+ }
+ return 0;
+}
+
+static int
+type_pf_head(struct ip_set *set, struct sk_buff *skb)
+{
+ const struct chash *h = set->data;
+ struct nlattr *nested;
+ size_t memsize;
+
+ read_lock_bh(&set->lock);
+ memsize = chash_memsize(h, with_timeout(h->timeout)
+ ? sizeof(struct type_pf_telem)
+ : sizeof(struct type_pf_elem),
+ set->family == AF_INET ? 32 : 128);
+ read_unlock_bh(&set->lock);
+
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested)
+ goto nla_put_failure;
+ NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE,
+ htonl(jhash_size(h->htable_bits)));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem));
+#ifdef IP_SET_HASH_WITH_NETMASK
+ if (h->netmask != HOST_MASK)
+ NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask);
+#endif
+ NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
+ htonl(atomic_read(&set->ref) - 1));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize));
+ if (with_timeout(h->timeout))
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout));
+ ipset_nest_end(skb, nested);
+
+ return 0;
+nla_put_failure:
+ return -EFAULT;
+}
+
+static int
+type_pf_list(struct ip_set *set,
+ struct sk_buff *skb, struct netlink_callback *cb)
+{
+ const struct chash *h = set->data;
+ struct nlattr *atd, *nested;
+ struct slist *n;
+ const struct type_pf_elem *data;
+ u32 first = cb->args[2];
+ int i;
+
+ atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
+ if (!atd)
+ return -EFAULT;
+ pr_debug("list hash set %s", set->name);
+ for (; cb->args[2] < jhash_size(h->htable_bits); cb->args[2]++) {
+ slist_for_each(n, &h->htable[cb->args[2]]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data))
+ break;
+ pr_debug("list hash %lu slist %p i %u",
+ cb->args[2], n, i);
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested) {
+ if (cb->args[2] == first) {
+ nla_nest_cancel(skb, atd);
+ return -EFAULT;
+ } else
+ goto nla_put_failure;
+ }
+ if (type_pf_data_list(skb, data))
+ goto nla_put_failure;
+ ipset_nest_end(skb, nested);
+ }
+ }
+ }
+ ipset_nest_end(skb, atd);
+ /* Set listing finished */
+ cb->args[2] = 0;
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nested);
+ ipset_nest_end(skb, atd);
+ return 0;
+}
+
+static int
+type_pf_kadt(struct ip_set *set, const struct sk_buff * skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
+static int
+type_pf_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags);
+
+static const struct ip_set_type_variant type_pf_variant __read_mostly = {
+ .kadt = type_pf_kadt,
+ .uadt = type_pf_uadt,
+ .adt = {
+ [IPSET_ADD] = type_pf_chash_add,
+ [IPSET_DEL] = type_pf_chash_del,
+ [IPSET_TEST] = type_pf_chash_test,
+ },
+ .destroy = type_pf_destroy,
+ .flush = type_pf_flush,
+ .head = type_pf_head,
+ .list = type_pf_list,
+ .resize = type_pf_resize,
+ .same_set = type_pf_same_set,
+};
+
+/* Flavour with timeout support */
+
+#define chash_tdata(n, i) \
+(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_telem))
+
+static inline u32
+type_pf_data_timeout(const struct type_pf_elem *data)
+{
+ const struct type_pf_telem *tdata =
+ (const struct type_pf_telem *) data;
+
+ return tdata->timeout;
+}
+
+static inline bool
+type_pf_data_expired(const struct type_pf_elem *data)
+{
+ const struct type_pf_telem *tdata =
+ (const struct type_pf_telem *) data;
+
+ return ip_set_timeout_expired(tdata->timeout);
+}
+
+static inline void
+type_pf_data_swap_timeout(struct type_pf_elem *src,
+ struct type_pf_elem *dst)
+{
+ struct type_pf_telem *x = (struct type_pf_telem *) src;
+ struct type_pf_telem *y = (struct type_pf_telem *) dst;
+
+ swap(x->timeout, y->timeout);
+}
+
+static inline void
+type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout)
+{
+ struct type_pf_telem *tdata = (struct type_pf_telem *) data;
+
+ tdata->timeout = ip_set_timeout_set(timeout);
+}
+
+static int
+type_pf_chash_treadd(struct chash *h, struct slist *t, u8 htable_bits,
+ const struct type_pf_elem *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct slist *n, *prev;
+ struct type_pf_elem *data;
+ void *tmp;
+ int i = 0, j = 0;
+ u32 hash = JHASH2(value, h->initval, htable_bits);
+
+ slist_for_each_prev(prev, n, &t[hash]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data)) {
+ tmp = n;
+ goto found;
+ }
+ }
+ j++;
+ }
+ if (j < h->chain_limit) {
+ tmp = kzalloc(h->array_size * sizeof(struct type_pf_telem)
+ + sizeof(struct slist), gfp_flags);
+ if (!tmp)
+ return -ENOMEM;
+ prev->next = (struct slist *) tmp;
+ data = chash_tdata(tmp, 0);
+ } else {
+ /* Rehashing */
+ return -EAGAIN;
+ }
+found:
+ type_pf_data_copy(data, value);
+ type_pf_data_timeout_set(data, timeout);
+ return 0;
+}
+
+static void
+type_pf_chash_del_telem(struct chash *h, struct slist *prev,
+ struct slist *n, int i)
+{
+ struct type_pf_elem *d, *data = chash_tdata(n, i);
+ struct slist *tmp;
+ int j;
+
+ pr_debug("del %u", i);
+ if (n->next != NULL) {
+ for (prev = n, tmp = n->next;
+ tmp->next != NULL;
+ prev = tmp, tmp = tmp->next)
+ /* Find last array */;
+ j = 0;
+ } else {
+ /* Already at last array */
+ tmp = n;
+ j = i;
+ }
+ /* Find last non-empty element */
+ for (; j < h->array_size - 1; j++)
+ if (type_pf_data_isnull(chash_tdata(tmp, j + 1)))
+ break;
+
+ d = chash_tdata(tmp, j);
+ if (!(tmp == n && i == j)) {
+ type_pf_data_swap(data, d);
+ type_pf_data_swap_timeout(data, d);
+ }
+#ifdef IP_SET_HASH_WITH_NETS
+ if (--h->nets[data->cidr-1].nets == 0)
+ del_cidr(h->nets, HOST_MASK, data->cidr);
+#endif
+ if (j == 0) {
+ prev->next = NULL;
+ kfree(tmp);
+ } else
+ type_pf_data_zero_out(d);
+
+ h->elements--;
+}
+
+static void
+type_pf_chash_expire(struct chash *h)
+{
+ struct slist *n, *prev;
+ struct type_pf_elem *data;
+ u32 i;
+ int j;
+
+ for (i = 0; i < jhash_size(h->htable_bits); i++)
+ slist_for_each_prev(prev, n, &h->htable[i])
+ for (j = 0; j < h->array_size; j++) {
+ data = chash_tdata(n, j);
+ if (type_pf_data_isnull(data))
+ break;
+ if (type_pf_data_expired(data)) {
+ pr_debug("expire %u/%u", i, j);
+ type_pf_chash_del_telem(h, prev, n, j);
+ }
+ }
+}
+
+static int
+type_pf_tresize(struct ip_set *set, gfp_t gfp_flags, bool retried)
+{
+ struct chash *h = set->data;
+ u8 htable_bits = h->htable_bits;
+ struct slist *t, *n;
+ const struct type_pf_elem *data;
+ u32 i, j;
+ u8 oflags, flags;
+ int ret;
+
+ /* Try to cleanup once */
+ if (!retried) {
+ i = h->elements;
+ write_lock_bh(&set->lock);
+ type_pf_chash_expire(set->data);
+ write_unlock_bh(&set->lock);
+ if (h->elements < i)
+ return 0;
+ }
+
+retry:
+ ret = 0;
+ htable_bits++;
+ if (!htable_bits)
+ /* In case we have plenty of memory :-) */
+ return -IPSET_ERR_HASH_FULL;
+ t = ip_set_alloc(jhash_size(htable_bits) * sizeof(struct slist),
+ gfp_flags, &flags);
+ if (!t)
+ return -ENOMEM;
+
+ write_lock_bh(&set->lock);
+ flags = oflags = set->flags;
+ for (i = 0; i < jhash_size(h->htable_bits); i++) {
+next_slot:
+ slist_for_each(n, &h->htable[i]) {
+ for (j = 0; j < h->array_size; j++) {
+ data = chash_tdata(n, j);
+ if (type_pf_data_isnull(data)) {
+ i++;
+ goto next_slot;
+ }
+ ret = type_pf_chash_treadd(h, t, htable_bits,
+ data, gfp_flags,
+ type_pf_data_timeout(data));
+ if (ret < 0) {
+ write_unlock_bh(&set->lock);
+ chash_destroy(t, htable_bits, flags);
+ if (ret == -EAGAIN)
+ goto retry;
+ return ret;
+ }
+ }
+ }
+ }
+
+ n = h->htable;
+ i = h->htable_bits;
+
+ h->htable = t;
+ h->htable_bits = htable_bits;
+ set->flags = flags;
+ write_unlock_bh(&set->lock);
+
+ chash_destroy(n, i, oflags);
+
+ return 0;
+}
+
+static int
+type_pf_chash_tadd(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ const struct type_pf_elem *d = value;
+ struct slist *n, *prev, *t = h->htable;
+ struct type_pf_elem *data;
+ void *tmp;
+ int i = 0, j = 0;
+ u32 hash;
+
+ if (h->elements >= h->maxelem)
+ /* FIXME: when set is full, we slow down here */
+ type_pf_chash_expire(h);
+#ifdef IP_SET_HASH_WITH_NETS
+ if (h->elements >= h->maxelem || h->nets[d->cidr-1].nets == UINT_MAX)
+#else
+ if (h->elements >= h->maxelem)
+#endif
+ return -IPSET_ERR_HASH_FULL;
+
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each_prev(prev, n, &t[hash]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data)
+ || type_pf_data_expired(data)) {
+ tmp = n;
+ goto found;
+ }
+ if (type_pf_data_equal(data, d))
+ return -IPSET_ERR_EXIST;
+ }
+ j++;
+ }
+ if (j < h->chain_limit) {
+ tmp = kzalloc(h->array_size * sizeof(struct type_pf_telem)
+ + sizeof(struct slist), gfp_flags);
+ if (!tmp)
+ return -ENOMEM;
+ prev->next = (struct slist *) tmp;
+ data = chash_tdata(tmp, 0);
+ } else {
+ /* Rehashing */
+ return -EAGAIN;
+ }
+found:
+ if (type_pf_data_isnull(data)) {
+ h->elements++;
+#ifdef IP_SET_HASH_WITH_NETS
+ } else {
+ if (--h->nets[data->cidr-1].nets == 0)
+ del_cidr(h->nets, HOST_MASK, data->cidr);
+ }
+ if (h->nets[d->cidr-1].nets++ == 0) {
+ add_cidr(h->nets, HOST_MASK, d->cidr);
+#endif
+ }
+ type_pf_data_copy(data, d);
+ type_pf_data_timeout_set(data, timeout);
+ return 0;
+}
+
+static int
+type_pf_chash_tdel(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ const struct type_pf_elem *d = value;
+ struct slist *n, *prev;
+ int i, ret = 0;
+ struct type_pf_elem *data;
+ u32 hash = JHASH2(value, h->initval, h->htable_bits);
+
+ slist_for_each_prev(prev, n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data))
+ return -IPSET_ERR_EXIST;
+ if (type_pf_data_equal(data, d)) {
+ if (type_pf_data_expired(data))
+ ret = -IPSET_ERR_EXIST;
+ type_pf_chash_del_telem(h, prev, n, i);
+ return ret;
+ }
+ }
+
+ return -IPSET_ERR_EXIST;
+}
+
+#ifdef IP_SET_HASH_WITH_NETS
+static inline int
+type_pf_chash_ttest_cidrs(struct ip_set *set,
+ struct type_pf_elem *d,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ struct type_pf_elem *data;
+ struct slist *n;
+ int i, j = 0;
+ u32 hash;
+ u8 host_mask = set->family == AF_INET ? 32 : 128;
+
+retry:
+ for (; j < host_mask - 1 && h->nets[j].cidr; j++) {
+ type_pf_data_netmask(d, h->nets[j].cidr);
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each(n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data)) {
+ j++;
+ goto retry;
+ }
+ if (type_pf_data_equal(data, d))
+ return !type_pf_data_expired(data);
+ }
+ }
+ return 0;
+}
+#endif
+
+static inline int
+type_pf_chash_ttest(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ struct type_pf_elem *data, *d = value;
+ struct slist *n;
+ int i;
+ u32 hash;
+#ifdef IP_SET_HASH_WITH_NETS
+ u8 host_mask = set->family == AF_INET ? 32 : 128;
+
+ if (d->cidr == host_mask)
+ return type_pf_chash_ttest_cidrs(set, d, gfp_flags,
+ timeout);
+#endif
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each(n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data))
+ return 0;
+ if (type_pf_data_equal(data, d))
+ return !type_pf_data_expired(data);
+ }
+ return 0;
+}
+
+static int
+type_pf_tlist(struct ip_set *set,
+ struct sk_buff *skb, struct netlink_callback *cb)
+{
+ const struct chash *h = set->data;
+ struct nlattr *atd, *nested;
+ struct slist *n;
+ const struct type_pf_elem *data;
+ u32 first = cb->args[2];
+ int i;
+
+ atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
+ if (!atd)
+ return -EFAULT;
+ for (; cb->args[2] < jhash_size(h->htable_bits); cb->args[2]++) {
+ slist_for_each(n, &h->htable[cb->args[2]]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ pr_debug("list %p %u", n, i);
+ if (type_pf_data_isnull(data))
+ break;
+ if (type_pf_data_expired(data))
+ continue;
+ pr_debug("do list %p %u", n, i);
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested) {
+ if (cb->args[2] == first) {
+ nla_nest_cancel(skb, atd);
+ return -EFAULT;
+ } else
+ goto nla_put_failure;
+ }
+ if (type_pf_data_tlist(skb, data))
+ goto nla_put_failure;
+ ipset_nest_end(skb, nested);
+ }
+ }
+ }
+ ipset_nest_end(skb, atd);
+ /* Set listing finished */
+ cb->args[2] = 0;
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nested);
+ ipset_nest_end(skb, atd);
+ return 0;
+}
+
+static const struct ip_set_type_variant type_pf_tvariant __read_mostly = {
+ .kadt = type_pf_kadt,
+ .uadt = type_pf_uadt,
+ .adt = {
+ [IPSET_ADD] = type_pf_chash_tadd,
+ [IPSET_DEL] = type_pf_chash_tdel,
+ [IPSET_TEST] = type_pf_chash_ttest,
+ },
+ .destroy = type_pf_destroy,
+ .flush = type_pf_flush,
+ .head = type_pf_head,
+ .list = type_pf_tlist,
+ .resize = type_pf_tresize,
+ .same_set = type_pf_same_set,
+};
+
+static void
+type_pf_gc(unsigned long ul_set)
+{
+ struct ip_set *set = (struct ip_set *) ul_set;
+ struct chash *h = set->data;
+
+ pr_debug("called");
+ write_lock_bh(&set->lock);
+ type_pf_chash_expire(h);
+ write_unlock_bh(&set->lock);
+
+ h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+ add_timer(&h->gc);
+}
+
+static inline void
+type_pf_gc_init(struct ip_set *set)
+{
+ struct chash *h = set->data;
+
+ init_timer(&h->gc);
+ h->gc.data = (unsigned long) set;
+ h->gc.function = type_pf_gc;
+ h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+ add_timer(&h->gc);
+ pr_debug("gc initialized, run in every %u", IPSET_GC_PERIOD(h->timeout));
+}
+
+#undef type_pf_data_equal
+#undef type_pf_data_isnull
+#undef type_pf_data_copy
+#undef type_pf_data_swap
+#undef type_pf_data_zero_out
+#undef type_pf_data_list
+#undef type_pf_data_tlist
+
+#undef type_pf_elem
+#undef type_pf_telem
+#undef type_pf_data_timeout
+#undef type_pf_data_expired
+#undef type_pf_data_swap_timeout
+#undef type_pf_data_netmask
+#undef type_pf_data_timeout_set
+
+#undef type_pf_chash_readd
+#undef type_pf_chash_del_elem
+#undef type_pf_chash_add
+#undef type_pf_chash_del
+#undef type_pf_chash_test_cidrs
+#undef type_pf_chash_test
+
+#undef type_pf_chash_treadd
+#undef type_pf_chash_del_telem
+#undef type_pf_chash_expire
+#undef type_pf_chash_tadd
+#undef type_pf_chash_tdel
+#undef type_pf_chash_ttest_cidrs
+#undef type_pf_chash_ttest
+
+#undef type_pf_resize
+#undef type_pf_tresize
+#undef type_pf_flush
+#undef type_pf_destroy
+#undef type_pf_head
+#undef type_pf_list
+#undef type_pf_tlist
+#undef type_pf_same_set
+#undef type_pf_kadt
+#undef type_pf_uadt
+#undef type_pf_gc
+#undef type_pf_gc_init
+#undef type_pf_variant
+#undef type_pf_tvariant
diff --git a/kernel/include/linux/netfilter/ip_set_getport.h b/kernel/include/linux/netfilter/ip_set_getport.h
index 855f12a..ffa89f1 100644
--- a/kernel/include/linux/netfilter/ip_set_getport.h
+++ b/kernel/include/linux/netfilter/ip_set_getport.h
@@ -8,8 +8,8 @@
#define IPSET_INVALID_PORT 65536
/* We must handle non-linear skbs */
-static uint32_t
-get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags)
+static bool
+get_port(u8 pf, const struct sk_buff *skb, bool src, u16 *port)
{
unsigned short protocol;
unsigned int protoff;
@@ -30,19 +30,19 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags)
protohdr = ipv6_find_hdr(skb, &protoff, -1, &frag_off);
if (protohdr < 0)
- return IPSET_INVALID_PORT;
+ return false;
protocol = protohdr;
fragoff = frag_off;
break;
}
default:
- return IPSET_INVALID_PORT;
+ return false;
}
/* See comments at tcp_match in ip_tables.c */
if (fragoff)
- return IPSET_INVALID_PORT;
+ return false;
switch (protocol) {
case IPPROTO_TCP: {
@@ -52,9 +52,10 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags)
th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
if (th == NULL)
/* No choice either */
- return IPSET_INVALID_PORT;
+ return false;
- return flags[0] & IPSET_SRC ? th->source : th->dest;
+ *port = src ? th->source : th->dest;
+ break;
}
case IPPROTO_UDP: {
struct udphdr _udph;
@@ -63,14 +64,16 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags)
uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
if (uh == NULL)
/* No choice either */
- return IPSET_INVALID_PORT;
+ return false;
- return flags[0] & IPSET_SRC ? uh->source : uh->dest;
+ *port = src ? uh->source : uh->dest;
+ break;
}
default:
- return IPSET_INVALID_PORT;
+ return false;
}
+ return true;
}
-#endif /* __KERNEL__ */
+#endif /* __KERNEL__ */
#endif /*_IP_SET_GETPORT_H*/
diff --git a/kernel/include/linux/netfilter/ip_set_hash.h b/kernel/include/linux/netfilter/ip_set_hash.h
index dd183b7..c1a6964 100644
--- a/kernel/include/linux/netfilter/ip_set_hash.h
+++ b/kernel/include/linux/netfilter/ip_set_hash.h
@@ -9,12 +9,11 @@ enum {
#ifdef __KERNEL__
-#define initval_t uint32_t
-
#define IPSET_DEFAULT_HASHSIZE 1024
+#define IPSET_MIMINAL_HASHSIZE 64
#define IPSET_DEFAULT_MAXELEM 65536
#define IPSET_DEFAULT_PROBES 4
-#define IPSET_DEFAULT_RESIZE 50
+#define IPSET_DEFAULT_RESIZE 100
#endif /* __KERNEL__ */
diff --git a/kernel/include/linux/netfilter/ip_set_jhash.h b/kernel/include/linux/netfilter/ip_set_jhash.h
index 90bfcc3..d5e0d6d 100644
--- a/kernel/include/linux/netfilter/ip_set_jhash.h
+++ b/kernel/include/linux/netfilter/ip_set_jhash.h
@@ -1,7 +1,6 @@
#ifndef _LINUX_JHASH_H
#define _LINUX_JHASH_H
-
-/* jhash.h: Jenkins hash support.
+/* jhash.c: Jenkins hash support.
*
* Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
*
@@ -17,141 +16,106 @@
* if SELF_TEST is defined. You can use this free for any purpose. It's in
* the public domain. It has no warranty.
*
- * Copyright (C) 2009 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* I've modified Bob's hash to be useful in the Linux kernel, and
- * any bugs present are my fault. Jozsef
+ * any bugs present are my fault. The generic jhash is left out intentionally.
+ * Jozsef
*/
-
-#define __rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
-
-/* __jhash_mix - mix 3 32-bit values reversibly. */
-#define __jhash_mix(a,b,c) \
-{ \
- a -= c; a ^= __rot(c, 4); c += b; \
- b -= a; b ^= __rot(a, 6); a += c; \
- c -= b; c ^= __rot(b, 8); b += a; \
- a -= c; a ^= __rot(c,16); c += b; \
- b -= a; b ^= __rot(a,19); a += c; \
- c -= b; c ^= __rot(b, 4); b += a; \
+#ifdef __KERNEL__
+#include <asm/byteorder.h>
+
+/* Best hash sizes are of power of two */
+#define jhash_size(n) ((u32)1<<(n))
+/* Mask the hash value, i.e (value & jhash_mask(n)) instead of (value % n) */
+#define jhash_mask(n) (jhash_size(n)-1)
+
+/* __jhash_rot - rotate 32 bit */
+#define __jhash_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/* __jhash_mix -- mix 3 32-bit values reversibly. */
+#define __jhash_mix(a,b,c) \
+{ \
+ a -= c; a ^= __jhash_rot(c, 4); c += b; \
+ b -= a; b ^= __jhash_rot(a, 6); a += c; \
+ c -= b; c ^= __jhash_rot(b, 8); b += a; \
+ a -= c; a ^= __jhash_rot(c,16); c += b; \
+ b -= a; b ^= __jhash_rot(a,19); a += c; \
+ c -= b; c ^= __jhash_rot(b, 4); b += a; \
}
/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
-#define __jhash_final(a,b,c) \
-{ \
- c ^= b; c -= __rot(b,14); \
- a ^= c; a -= __rot(c,11); \
- b ^= a; b -= __rot(a,25); \
- c ^= b; c -= __rot(b,16); \
- a ^= c; a -= __rot(c,4); \
- b ^= a; b -= __rot(a,14); \
- c ^= b; c -= __rot(b,24); \
+#define __jhash_final(a,b,c) \
+{ \
+ c ^= b; c -= __jhash_rot(b,14); \
+ a ^= c; a -= __jhash_rot(c,11); \
+ b ^= a; b -= __jhash_rot(a,25); \
+ c ^= b; c -= __jhash_rot(b,16); \
+ a ^= c; a -= __jhash_rot(c,4); \
+ b ^= a; b -= __jhash_rot(a,14); \
+ c ^= b; c -= __jhash_rot(b,24); \
}
-/* An arbitrary value */
-#define JHASH_RANDOM_PARAM 0xdeadbeef
-
-/* The most generic version, hashes an arbitrary sequence
- * of bytes. No alignment or length assumptions are made about
- * the input key. The result depends on endianness.
- */
-static inline u32 jhash(const void *key, u32 length, u32 initval)
-{
- u32 a,b,c;
- const u8 *k = key;
-
- /* Set up the internal state */
- a = b = c = JHASH_RANDOM_PARAM + length + initval;
-
- /* all but the last block: affect some 32 bits of (a,b,c) */
- while (length > 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);
- length -= 12;
- k += 12;
- }
-
- /* last block: affect all 32 bits of (c) */
- /* all the case statements fall through */
- switch (length) {
- case 12: c += (u32)k[11]<<24;
- case 11: c += (u32)k[10]<<16;
- case 10: c += (u32)k[9]<<8;
- case 9 : c += k[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_final(a, b, c);
- case 0 :
- break;
- }
-
- return c;
-}
+#define JHASH_INITVAL 0xdeadbeef
-/* A special optimized version that handles 1 or more of u32s.
- * The length parameter here is the number of u32s in the key.
+/* jhash2 - hash an array of u32's
+ * @k: the key which must be an array of u32's
+ * @length: the number of u32's in the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * Returns the hash value of the key.
*/
static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
{
u32 a, b, c;
/* Set up the internal state */
- a = b = c = JHASH_RANDOM_PARAM + (length<<2) + initval;
+ a = b = c = JHASH_INITVAL + (length<<2) + initval;
- /* handle most of the key */
+ /* Handle most of the key */
while (length > 3) {
a += k[0];
b += k[1];
c += k[2];
- __jhash_mix(a, b, c);
+ __jhash_mix(a,b,c);
length -= 3;
k += 3;
}
-
- /* handle the last 3 u32's */
- /* all the case statements fall through */
- switch (length) {
+
+ /* Handle the last 3 u32's: all the case statements fall through */
+ switch(length) {
case 3: c += k[2];
case 2: b += k[1];
case 1: a += k[0];
- __jhash_final(a, b, c);
- case 0: /* case 0: nothing left to add */
+ __jhash_final(a,b,c);
+ case 0: /* Nothing left to add */
break;
}
return c;
}
-/* A special ultra-optimized versions that knows they are hashing exactly
- * 3, 2 or 1 word(s).
- */
+/* jhash_3words - hash exactly 3, 2 or 1 word(s) */
static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
{
- a += JHASH_RANDOM_PARAM + initval;
- b += JHASH_RANDOM_PARAM + initval;
- c += JHASH_RANDOM_PARAM + initval;
-
- __jhash_final(a, b, c);
+ a += JHASH_INITVAL;
+ b += JHASH_INITVAL;
+ c += initval;
+ __jhash_final(a,b,c);
+
return c;
}
-
static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
{
- return jhash_3words(0, a, b, initval);
+ return jhash_3words(a, b, 0, initval);
}
static inline u32 jhash_1word(u32 a, u32 initval)
{
- return jhash_3words(0, 0, a, initval);
+ return jhash_3words(a, 0, 0, initval);
}
+#endif /* __KERNEL__ */
+
#endif /* _LINUX_JHASH_H */
diff --git a/kernel/include/linux/netfilter/ip_set_kernel.h b/kernel/include/linux/netfilter/ip_set_kernel.h
new file mode 100644
index 0000000..d6e033b
--- /dev/null
+++ b/kernel/include/linux/netfilter/ip_set_kernel.h
@@ -0,0 +1,20 @@
+#ifndef _IP_SET_KERNEL_H
+#define _IP_SET_KERNEL_H
+
+/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef __KERNEL__
+
+/* Complete debug messages */
+#define pr_fmt(fmt) "%s %s[%i]: " fmt "\n", __FILE__, __func__, __LINE__
+
+#include <linux/kernel.h>
+
+#endif /* __KERNEL__ */
+
+#endif /*_IP_SET_H */
diff --git a/kernel/include/linux/netfilter/ip_set_list.h b/kernel/include/linux/netfilter/ip_set_list.h
new file mode 100644
index 0000000..c40643e
--- /dev/null
+++ b/kernel/include/linux/netfilter/ip_set_list.h
@@ -0,0 +1,21 @@
+#ifndef __IP_SET_LIST_H
+#define __IP_SET_LIST_H
+
+/* List type specific error codes */
+enum {
+ IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC,
+ IPSET_ERR_LOOP,
+ IPSET_ERR_BEFORE,
+ IPSET_ERR_NAMEREF,
+ IPSET_ERR_LIST_FULL,
+ IPSET_ERR_REF_EXIST,
+};
+
+#ifdef __KERNEL__
+
+#define IP_SET_LIST_DEFAULT_SIZE 8
+#define IP_SET_LIST_MIN_SIZE 4
+
+#endif /* __KERNEL__ */
+
+#endif /* __IP_SET_LIST_H */
diff --git a/kernel/include/linux/netfilter/ip_set_slist.h b/kernel/include/linux/netfilter/ip_set_slist.h
new file mode 100644
index 0000000..abc5afe
--- /dev/null
+++ b/kernel/include/linux/netfilter/ip_set_slist.h
@@ -0,0 +1,86 @@
+#ifndef _IP_SET_SLIST_H
+#define _IP_SET_SLIST_H
+
+#include <linux/stddef.h>
+#include <linux/prefetch.h>
+#include <asm/system.h>
+
+/*
+ * Single linked lists with a single pointer.
+ * Mostly useful for hash tables where the two pointer list head
+ * and list node is too wasteful.
+ */
+
+struct slist {
+ struct slist *next;
+};
+
+#define SLIST(name) struct slist name = { .next = NULL }
+#define INIT_SLIST(ptr) ((ptr)->next = NULL)
+
+#define slist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define slist_for_each(pos, head) \
+ for (pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define slist_for_each_prev(prev, pos, head) \
+ for (prev = head, pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \
+ prev = pos, pos = pos->next)
+
+#define slist_for_each_safe(pos, n, head) \
+ for (pos = (head)->next; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+/**
+ * slist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct slist to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the slist within the struct.
+ */
+#define slist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * slist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct slist to use as a loop cursor.
+ * @member: the name of the slist within the struct.
+ */
+#define slist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * slist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct slist to use as a loop cursor.
+ * @member: the name of the slist within the struct.
+ */
+#define slist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * slist_for_each_entry_safe - iterate over list of given type safe against
+ * removal of list entry
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct slist to use as a loop cursor.
+ * @n: another &struct slist to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the slist within the struct.
+ */
+#define slist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->next; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+#endif /* _IP_SET_SLIST_H */
diff --git a/kernel/include/linux/netfilter/ip_set_timeout.h b/kernel/include/linux/netfilter/ip_set_timeout.h
index da18875..bf1cbf6 100644
--- a/kernel/include/linux/netfilter/ip_set_timeout.h
+++ b/kernel/include/linux/netfilter/ip_set_timeout.h
@@ -10,21 +10,33 @@
#ifdef __KERNEL__
-/* How often should the gc be run at a minimum */
+/* How often should the gc be run by default */
#define IPSET_GC_TIME (3 * 60)
/* Timeout period depending on the timeout value of the given set */
#define IPSET_GC_PERIOD(timeout) \
- max_t(uint32_t, (timeout)/10, IPSET_GC_TIME)
+ ((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1)
-/* How much msec to sleep before retrying to destroy gc timer */
-#define IPSET_DESTROY_TIMER_SLEEP 10
+/* Set is defined without timeout support */
+#define IPSET_NO_TIMEOUT UINT_MAX
-/* Timing out etries: unset and permanent */
+#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT)
+
+static inline unsigned int
+ip_set_timeout_uget(struct nlattr *tb)
+{
+ unsigned int timeout = ip_set_get_h32(tb);
+
+ return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout;
+}
+
+#ifdef IP_SET_BITMAP_TIMEOUT
+
+/* Bitmap entry is unset */
#define IPSET_ELEM_UNSET 0
+/* Bitmap entry is set with no timeout value */
#define IPSET_ELEM_PERMANENT UINT_MAX/2
-#ifdef IP_SET_BITMAP_TIMEOUT
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
@@ -42,7 +54,7 @@ ip_set_timeout_expired(unsigned long timeout)
}
static inline unsigned long
-ip_set_timeout_set(uint32_t timeout)
+ip_set_timeout_set(u32 timeout)
{
unsigned long t;
@@ -56,7 +68,7 @@ ip_set_timeout_set(uint32_t timeout)
return t;
}
-static inline uint32_t
+static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
@@ -64,6 +76,9 @@ ip_set_timeout_get(unsigned long timeout)
#else
+/* Hash entry is set with no timeout value */
+#define IPSET_ELEM_UNSET 0
+
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
@@ -77,7 +92,7 @@ ip_set_timeout_expired(unsigned long timeout)
}
static inline unsigned long
-ip_set_timeout_set(uint32_t timeout)
+ip_set_timeout_set(u32 timeout)
{
unsigned long t;
@@ -91,7 +106,7 @@ ip_set_timeout_set(uint32_t timeout)
return t;
}
-static inline uint32_t
+static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_UNSET ? 0 : (timeout - jiffies)/HZ;
diff --git a/kernel/include/linux/netfilter/ipt_set.h b/kernel/include/linux/netfilter/ipt_set.h
deleted file mode 100644
index 2a18b93..0000000
--- a/kernel/include/linux/netfilter/ipt_set.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#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 + 1];
-};
-
-/* 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*/
diff --git a/kernel/include/linux/netfilter/xt_set.h b/kernel/include/linux/netfilter/xt_set.h
new file mode 100644
index 0000000..949fa59
--- /dev/null
+++ b/kernel/include/linux/netfilter/xt_set.h
@@ -0,0 +1,55 @@
+#ifndef _XT_SET_H
+#define _XT_SET_H
+
+#include <linux/netfilter/ip_set.h>
+
+/* Revision 0 interface: backward compatible with netfilter/iptables */
+
+/*
+ * Option flags for kernel operations (xt_set_info_v0)
+ */
+#define IPSET_SRC 0x01 /* Source match/add */
+#define IPSET_DST 0x02 /* Destination match/add */
+#define IPSET_MATCH_INV 0x04 /* Inverse matching */
+
+struct xt_set_info_v0 {
+ ip_set_id_t index;
+ union {
+ u_int32_t flags[IPSET_DIM_MAX + 1];
+ struct {
+ u_int32_t __flags[IPSET_DIM_MAX];
+ u_int8_t dim;
+ u_int8_t flags;
+ } compat;
+ } u;
+};
+
+/* match and target infos */
+struct xt_set_info_match_v0 {
+ struct xt_set_info_v0 match_set;
+};
+
+struct xt_set_info_target_v0 {
+ struct xt_set_info_v0 add_set;
+ struct xt_set_info_v0 del_set;
+};
+
+/* Revision 1: current interface to netfilter/iptables */
+
+struct xt_set_info {
+ ip_set_id_t index;
+ u_int8_t dim;
+ u_int8_t flags;
+};
+
+/* match and target infos */
+struct xt_set_info_match {
+ struct xt_set_info match_set;
+};
+
+struct xt_set_info_target {
+ struct xt_set_info add_set;
+ struct xt_set_info del_set;
+};
+
+#endif /*_XT_SET_H*/
diff --git a/kernel/ip_set.c b/kernel/ip_set.c
index 3af8fce..5bf331e 100644
--- a/kernel/ip_set.c
+++ b/kernel/ip_set.c
@@ -9,25 +9,26 @@
/* Kernel module for IP set management */
+#include <linux/netfilter/ip_set_kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/kernel.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/netlink.h>
+#include <linux/rculist.h>
#include <net/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/ip_set.h>
-#include <linux/netfilter/ip_set_jhash.h>
-static struct list_head ip_set_type_list; /* all registered sets */
+static struct list_head ip_set_type_list; /* all registered set types */
+static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */
+
static struct ip_set **ip_set_list; /* all individual sets */
-static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_lists */
-static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX;
+static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */
#define STREQ(a,b) (strncmp(a,b,IPSET_MAXNAMELEN) == 0)
@@ -43,31 +44,146 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET);
/*
* The set types are implemented in modules and registered set types
* can be found in ip_set_type_list. Adding/deleting types is
- * serialized by ip_set_type_list_lock/ip_set_type_list_unlock.
+ * serialized by ip_set_type_mutex.
*/
static inline void
-ip_set_type_list_lock(void)
+ip_set_type_lock(void)
{
mutex_lock(&ip_set_type_mutex);
}
static inline void
-ip_set_type_list_unlock(void)
+ip_set_type_unlock(void)
{
mutex_unlock(&ip_set_type_mutex);
}
+/* Register and deregister settype */
+
+static inline struct ip_set_type *
+find_set_type(const char *name, u8 family, u8 revision)
+{
+ struct ip_set_type *type;
+
+ list_for_each_entry_rcu(type, &ip_set_type_list, list)
+ if (STREQ(type->name, name)
+ && (type->family == family || type->family == AF_UNSPEC)
+ && type->revision == revision)
+ return type;
+ return NULL;
+}
+
+/* Find a set type so that rcu_read_lock() is called by the function.
+ * If we succeeded, the RCU lock is NOT released and the caller
+ * must release it later.
+ */
+static struct ip_set_type *
+find_set_type_rcu(const char *name, u8 family, u8 revision)
+{
+ struct ip_set_type *type;
+
+ rcu_read_lock();
+ type = find_set_type(name, family, revision);
+ if (type == NULL)
+ rcu_read_unlock();
+
+ return type;
+}
+
+/* Find a given set type by name and family together
+ * with the supported minimal and maximum revisions.
+ */
+static bool
+find_set_type_minmax(const char *name, u8 family,
+ u8 *min, u8 *max)
+{
+ struct ip_set_type *type;
+ bool ret = false;
+
+ *min = *max = 0;
+ rcu_read_lock();
+ list_for_each_entry_rcu(type, &ip_set_type_list, list)
+ if (STREQ(type->name, name)
+ && (type->family == family || type->family == AF_UNSPEC)) {
+ ret = true;
+ if (type->revision < *min)
+ *min = type->revision;
+ else if (type->revision > *max)
+ *max = type->revision;
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+#define family_name(f) ((f) == AF_INET ? "inet" : \
+ (f) == AF_INET6 ? "inet6" : "any")
+
+/* Register a set type structure. The type is identified by
+ * the unique triple of name, family and revision.
+ */
+int
+ip_set_type_register(struct ip_set_type *type)
+{
+ int ret = 0;
+
+ if (type->protocol != IPSET_PROTOCOL) {
+ pr_warning("ip_set type %s, family %s, revision %u uses "
+ "wrong protocol version %u (want %u)\n",
+ type->name, family_name(type->family),
+ type->revision, type->protocol, IPSET_PROTOCOL);
+ return -EINVAL;
+ }
+
+ ip_set_type_lock();
+ if (find_set_type(type->name, type->family, type->revision)) {
+ /* Duplicate! */
+ pr_warning("ip_set type %s, family %s, revision %u "
+ "already registered!\n", type->name,
+ family_name(type->family), type->revision);
+ ret = -EINVAL;
+ goto unlock;
+ }
+ list_add_rcu(&type->list, &ip_set_type_list);
+ pr_debug("type %s, family %s, revision %u registered.",
+ type->name, family_name(type->family), type->revision);
+unlock:
+ ip_set_type_unlock();
+ return ret;
+}
+EXPORT_SYMBOL(ip_set_type_register);
+
+/* Unregister a set type. There's a small race with ip_set_create */
+void
+ip_set_type_unregister(struct ip_set_type *type)
+{
+ ip_set_type_lock();
+ if (!find_set_type(type->name, type->family, type->revision)) {
+ pr_warning("ip_set type %s, family %s, revision %u "
+ "not registered\n", type->name,
+ family_name(type->family), type->revision);
+ goto unlock;
+ }
+ list_del_rcu(&type->list);
+ pr_debug("type %s, family %s, revision %u unregistered.",
+ type->name, family_name(type->family), type->revision);
+unlock:
+ ip_set_type_unlock();
+
+ synchronize_rcu();
+}
+EXPORT_SYMBOL(ip_set_type_unregister);
+
/*
* Creating/destroying/renaming/swapping affect the existence and
- * integrity of a set. All of these can be executed from userspace only
- * and serialized by nfnl_lock/nfnl_unlock indirectly from nfnetlink.
+ * the properties of a set. All of these can be executed from userspace
+ * only and serialized by the nfnl mutex indirectly from nfnetlink.
*
* Sets are identified by their index in ip_set_list and the index
* is used by the external references (set/SET netfilter modules).
*
- * The set behind an index may change by swapping.
- *
+ * The set behind an index may change by swapping only, from userspace.
*/
static inline void
@@ -82,264 +198,306 @@ __ip_set_put(ip_set_id_t index)
atomic_dec(&ip_set_list[index]->ref);
}
-/* Add, del and test set entries from kernel */
+/*
+ * Add, del and test set entries from kernel.
+ *
+ * The set behind the index must exist and must be referenced
+ * so it can't be destroyed (or changed) under our foot.
+ */
int
ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags)
+ u8 family, u8 dim, u8 flags)
{
- struct ip_set *set;
+ struct ip_set *set = ip_set_list[index];
int ret = 0;
- rcu_read_lock();
- set = rcu_dereference(ip_set_list[index]);
- D("set %s, index %u", set->name, index);
+ BUG_ON(set == NULL || atomic_read(&set->ref) == 0);
+ pr_debug("set %s, index %u", set->name, index);
+
+ if (dim < set->type->dimension
+ || !(family == set->family || set->family == AF_UNSPEC))
+ return 0;
read_lock_bh(&set->lock);
- ret = set->variant->kadt(set, skb, IPSET_TEST, family, flags);
+ ret = set->variant->kadt(set, skb, IPSET_TEST, family, dim, flags);
read_unlock_bh(&set->lock);
if (ret == -EAGAIN) {
/* Type requests element to be re-added */
+ pr_debug("element must be competed, ADD is triggered");
write_lock_bh(&set->lock);
- set->variant->kadt(set, skb, IPSET_ADD, family, flags);
+ set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags);
write_unlock_bh(&set->lock);
ret = 1;
}
- rcu_read_unlock();
-
+ /* Convert error codes to nomatch */
return (ret < 0 ? 0 : ret);
}
+EXPORT_SYMBOL(ip_set_test);
int
ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags)
+ u8 family, u8 dim, u8 flags)
{
- struct ip_set *set;
+ struct ip_set *set = ip_set_list[index];
int ret = 0, retried = 0;
-retry:
- rcu_read_lock();
- set = rcu_dereference(ip_set_list[index]);
- D("set %s, index %u", set->name, index);
+ BUG_ON(set == NULL || atomic_read(&set->ref) == 0);
+ pr_debug("set %s, index %u", set->name, index);
+ if (dim < set->type->dimension
+ || !(family == set->family || set->family == AF_UNSPEC))
+ return 0;
+
+retry:
write_lock_bh(&set->lock);
- ret = set->variant->kadt(set, skb, IPSET_ADD, family, flags);
+ ret = set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags);
write_unlock_bh(&set->lock);
- rcu_read_unlock();
/* Retry function must be called without holding any lock */
if (ret == -EAGAIN
&& set->variant->resize
- && (ret = set->variant->resize(set, retried++)) == 0)
+ && (ret = set->variant->resize(set, GFP_ATOMIC, retried++)) == 0)
goto retry;
return ret;
}
+EXPORT_SYMBOL(ip_set_add);
int
ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags)
+ u8 family, u8 dim, u8 flags)
{
- struct ip_set *set;
+ struct ip_set *set = ip_set_list[index];
int ret = 0;
- rcu_read_lock();
- set = rcu_dereference(ip_set_list[index]);
- D("set %s, index %u", set->name, index);
+ BUG_ON(set == NULL || atomic_read(&set->ref) == 0);
+ pr_debug("set %s, index %u", set->name, index);
+
+ if (dim < set->type->dimension
+ || !(family == set->family || set->family == AF_UNSPEC))
+ return 0;
write_lock_bh(&set->lock);
- ret = set->variant->kadt(set, skb, IPSET_DEL, family, flags);
+ ret = set->variant->kadt(set, skb, IPSET_DEL, family, dim, flags);
write_unlock_bh(&set->lock);
-
- rcu_read_unlock();
return ret;
}
+EXPORT_SYMBOL(ip_set_del);
-/* Register and deregister settype */
-
-#define family_name(f) ((f) == AF_INET ? "inet" : \
- (f) == AF_INET6 ? "inet6" : "any")
-
-static inline struct ip_set_type *
-find_set_type(const char *name, uint8_t family, uint8_t revision)
+/*
+ * Find set by name, reference it once. The reference makes sure the
+ * thing pointed to, does not go away under our feet.
+ *
+ * The nfnl mutex must already be activated.
+ */
+ip_set_id_t
+ip_set_get_byname(const char *name, struct ip_set **set)
{
- struct ip_set_type *type;
-
- list_for_each_entry(type, &ip_set_type_list, list)
- if (STREQ(type->name, name)
- && (type->family == family || type->family == AF_UNSPEC)
- && type->revision == revision)
- return type;
- return NULL;
-}
+ ip_set_id_t i, index = IPSET_INVALID_ID;
+ struct ip_set *s;
-int
-ip_set_type_register(struct ip_set_type *type)
-{
- int ret = 0;
-
- if (type->protocol != IPSET_PROTOCOL) {
- printk("set type %s, family %s, revision %u uses "
- "wrong protocol version %u (want %u)\n",
- type->name, family_name(type->family), type->revision,
- type->protocol, IPSET_PROTOCOL);
- return -EINVAL;
+ for (i = 0; i < ip_set_max; i++) {
+ s = ip_set_list[i];
+ if (s != NULL && STREQ(s->name, name)) {
+ __ip_set_get(i);
+ index = i;
+ *set = s;
+ }
}
- ip_set_type_list_lock();
- if (find_set_type(type->name, type->family, type->revision)) {
- /* Duplicate! */
- printk("type %s, family %s, revision %u already registered!\n",
- type->name, family_name(type->family), type->revision);
- ret = -EINVAL;
- goto unlock;
- }
- list_add(&type->list, &ip_set_type_list);
- D("type %s, family %s, revision %u registered.",
- type->name, family_name(type->family), type->revision);
-unlock:
- ip_set_type_list_unlock();
- return ret;
+ return index;
}
+EXPORT_SYMBOL(ip_set_get_byname);
+/*
+ * 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.
+ *
+ * The nfnl mutex must already be activated.
+ */
void
-ip_set_type_unregister(struct ip_set_type *type)
+ip_set_put_byindex(ip_set_id_t index)
{
- ip_set_type_list_lock();
- if (!find_set_type(type->name, type->family, type->revision)) {
- printk("type %s, family %s, revision %u not registered\n",
- type->name, family_name(type->family), type->revision);
- goto unlock;
+ if (ip_set_list[index] != NULL) {
+ BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0);
+ __ip_set_put(index);
}
- list_del(&type->list);
- D("type %s, family %s, revision %u unregistered.",
- type->name, family_name(type->family), type->revision);
-unlock:
- ip_set_type_list_unlock();
}
+EXPORT_SYMBOL(ip_set_put_byindex);
+
+/*
+ * Get the name of a set behind a set index.
+ * We assume the set is referenced, so it does exist and
+ * can't be destroyed. The set cannot be renamed due to
+ * the referencing either.
+ *
+ * The nfnl mutex must already be activated.
+ */
+const char *
+ip_set_name_byindex(ip_set_id_t index)
+{
+ struct ip_set *set = ip_set_list[index];
+
+ BUG_ON(set == NULL);
+ BUG_ON(atomic_read(&set->ref) == 0);
+
+ /* Referenced, so it's safe */
+ return set->name;
+}
+EXPORT_SYMBOL(ip_set_name_byindex);
-/* Get/put a set with referencing */
+/*
+ * Routines to call by external subsystems, which do not
+ * call nfnl_lock for us.
+ */
/*
* 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*().
+ * thing pointed to, does not go away under our feet.
+ *
+ * The nfnl mutex is used in the function.
*/
ip_set_id_t
-ip_set_get_byname(const char *name)
+ip_set_nfnl_get(const char *name)
{
- ip_set_id_t i, index = IPSET_INVALID_ID;
-
- nfnl_lock();
- for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++)
- if (STREQ(ip_set_list[i]->name, name)) {
- __ip_set_get(i);
- index = i;
- }
+ struct ip_set *s;
+ ip_set_id_t index;
+
+ nfnl_lock();
+ index = ip_set_get_byname(name, &s);
+ nfnl_unlock();
+
+ return index;
+}
+EXPORT_SYMBOL(ip_set_nfnl_get);
+
+/*
+ * Find set by index, reference it once. The reference makes sure the
+ * thing pointed to, does not go away under our feet.
+ *
+ * The nfnl mutex is used in the function.
+ */
+ip_set_id_t
+ip_set_nfnl_get_byindex(ip_set_id_t index)
+{
+ if (index > ip_set_max)
+ return IPSET_INVALID_ID;
+
+ nfnl_lock();
+ if (ip_set_list[index])
+ __ip_set_get(index);
+ else
+ index = IPSET_INVALID_ID;
nfnl_unlock();
return index;
}
+EXPORT_SYMBOL(ip_set_nfnl_get_byindex);
/*
* 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.
+ *
+ * The nfnl mutex is used in the function.
*/
void
-ip_set_put_byindex(ip_set_id_t index)
+ip_set_nfnl_put(ip_set_id_t index)
{
nfnl_lock();
- if (ip_set_list[index])
+ if (ip_set_list[index] != NULL) {
+ BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0);
__ip_set_put(index);
+ }
nfnl_unlock();
}
+EXPORT_SYMBOL(ip_set_nfnl_put);
-static ip_set_id_t
-find_set_id(const char *name)
+/*
+ * Communication protocol with userspace over netlink.
+ *
+ * We already locked by nfnl_lock.
+ */
+
+static inline bool
+protocol_failed(const struct nlattr * const tb[])
{
- ip_set_id_t i, index = IPSET_INVALID_ID;
-
- for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) {
- if (ip_set_list[i] != NULL
- && STREQ(ip_set_list[i]->name, name))
- index = i;
- }
- return index;
+ return !tb[IPSET_ATTR_PROTOCOL]
+ || nla_get_u8(tb[IPSET_ATTR_PROTOCOL]) != IPSET_PROTOCOL;
}
-static ip_set_id_t
-find_set_id_rcu(const char *name)
+static inline u32
+flag_exist(const struct nlmsghdr *nlh)
{
- ip_set_id_t i, index = IPSET_INVALID_ID;
- struct ip_set *set;
-
- for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) {
- set = rcu_dereference(ip_set_list[i]);
- if (set != NULL && STREQ(set->name, name))
- index = i;
- }
- return index;
+ return nlh->nlmsg_flags & NLM_F_EXCL ? 0 : IPSET_FLAG_EXIST;
}
-static struct ip_set *
-find_set(const char *name)
+static inline bool
+flag_nested(const struct nlattr *nla)
{
- ip_set_id_t index = find_set_id(name);
-
- return index == IPSET_INVALID_ID ? NULL : ip_set_list[index];
+ return nla->nla_type & NLA_F_NESTED;
}
-/* Communication protocol with userspace over netlink */
+static struct nlmsghdr *
+start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags,
+ enum ipset_cmd cmd)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfmsg;
+
+ nlh = nlmsg_put(skb, pid, seq, cmd | (NFNL_SUBSYS_IPSET << 8),
+ sizeof(*nfmsg), flags);
+ if (nlh == NULL)
+ return NULL;
+
+ nfmsg = nlmsg_data(nlh);
+ nfmsg->nfgen_family = AF_INET;
+ nfmsg->version = NFNETLINK_V0;
+ nfmsg->res_id = 0;
+
+ return nlh;
+}
/* Create a set */
static const struct nla_policy
ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = {
[IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 },
- [IPSET_ATTR_SETNAME] = { .type = NLA_STRING,
- .len = IPSET_MAXNAMELEN },
- [IPSET_ATTR_TYPENAME] = { .type = NLA_STRING,
- .len = IPSET_MAXNAMELEN },
+ [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING,
+ .len = IPSET_MAXNAMELEN -1 },
+ [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING,
+ .len = IPSET_MAXNAMELEN - 1},
[IPSET_ATTR_REVISION] = { .type = NLA_U8 },
[IPSET_ATTR_FAMILY] = { .type = NLA_U8 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_DATA] = { .type = NLA_NESTED },
};
-static inline bool
-protocol_failed(const struct nlattr * const tb[])
-{
- return !tb[IPSET_ATTR_PROTOCOL]
- || nla_get_u8(tb[IPSET_ATTR_PROTOCOL]) != IPSET_PROTOCOL;
-}
-
-static inline uint32_t
-flag_exist(const struct nlmsghdr *nlh)
-{
- return nlh->nlmsg_flags & NLM_F_EXCL ? 0 : IPSET_FLAG_EXIST;
-}
-
-static inline bool
-flag_nested(const struct nlattr *nla)
+static ip_set_id_t
+find_set_id(const char *name)
{
- return nla->nla_type & NLA_F_NESTED;
+ ip_set_id_t i, index = IPSET_INVALID_ID;
+ struct ip_set *set;
+
+ for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) {
+ set = ip_set_list[i];
+ if (set != NULL && STREQ(set->name, name))
+ index = i;
+ }
+ return index;
}
-static struct ip_set_type *
-find_set_type_lock(const char *name, uint8_t family, uint8_t revision)
+static inline struct ip_set *
+find_set(const char *name)
{
- struct ip_set_type *type;
-
- ip_set_type_list_lock();
- type = find_set_type(name, family, revision);
- if (type == NULL)
- ip_set_type_list_unlock();
+ ip_set_id_t index = find_set_id(name);
- return type;
+ return index == IPSET_INVALID_ID ? NULL : ip_set_list[index];
}
static int
@@ -364,30 +522,10 @@ find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set)
return 0;
}
-static struct nlmsghdr *
-start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags,
- enum ipset_cmd cmd)
-{
- struct nlmsghdr *nlh;
- struct nfgenmsg *nfmsg;
-
- nlh = nlmsg_put(skb, pid, seq, cmd | (NFNL_SUBSYS_IPSET << 8),
- sizeof(*nfmsg), flags);
- if (nlh == NULL)
- return NULL;
-
- nfmsg = nlmsg_data(nlh);
- nfmsg->nfgen_family = AF_INET;
- nfmsg->version = NFNETLINK_V0;
- nfmsg->res_id = 0;
-
- return nlh;
-}
-
static inline void
load_type_module(const char *typename)
{
- D("try to load ip_set_%s", typename);
+ pr_debug("try to load ip_set_%s", typename);
request_module("ip_set_%s", typename);
}
@@ -399,8 +537,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
struct ip_set *set, *clash;
ip_set_id_t index = IPSET_INVALID_ID;
const char *name, *typename;
- uint8_t family, revision;
- uint32_t flags = flag_exist(nlh);
+ u8 family, revision;
+ u32 flags = flag_exist(nlh);
int ret = 0, len;
if (unlikely(protocol_failed(attr)
@@ -416,8 +554,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
typename = nla_data(attr[IPSET_ATTR_TYPENAME]);
family = nla_get_u8(attr[IPSET_ATTR_FAMILY]);
revision = nla_get_u8(attr[IPSET_ATTR_REVISION]);
- D("setname: %s, typename: %s, family: %s, revision: %u",
- name, typename, family_name(family), revision);
+ pr_debug("setname: %s, typename: %s, family: %s, revision: %u",
+ name, typename, family_name(family), revision);
/*
* First, and without any locks, allocate and initialize
@@ -429,6 +567,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
rwlock_init(&set->lock);
strncpy(set->name, name, IPSET_MAXNAMELEN);
atomic_set(&set->ref, 0);
+ set->family = family;
/*
* Next, check that we know the type, and take
@@ -438,31 +577,32 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
* After referencing the type, we try to create the type
* specific part of the set without holding any locks.
*/
- set->type = find_set_type_lock(typename, family, revision);
+ set->type = find_set_type_rcu(typename, family, revision);
if (set->type == NULL) {
/* Try loading the module */
load_type_module(typename);
- set->type = find_set_type_lock(typename, family, revision);
+ set->type = find_set_type_rcu(typename, family, revision);
if (set->type == NULL) {
- printk("Can't find type %s, family %s, revision %u:"
- " set '%s' not created",
- typename, family_name(family), revision, name);
+ pr_warning("Can't find ip_set type %s, family %s, "
+ "revision %u: set '%s' not created",
+ typename, family_name(family), revision,
+ name);
ret = -IPSET_ERR_FIND_TYPE;
goto out;
}
}
if (!try_module_get(set->type->me)) {
- ip_set_type_list_unlock();
+ rcu_read_unlock();
ret = -EFAULT;
goto out;
}
- ip_set_type_list_unlock();
+ rcu_read_unlock();
/*
* Without holding any locks, create private part.
*/
len = attr[IPSET_ATTR_DATA] ? nla_len(attr[IPSET_ATTR_DATA]) : 0;
- D("data len: %u", len);
+ pr_debug("data len: %u", len);
ret = set->type->create(set, attr[IPSET_ATTR_DATA] ?
nla_data(attr[IPSET_ATTR_DATA]) : NULL, len,
flags);
@@ -482,7 +622,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
&& (flags & IPSET_FLAG_EXIST)
&& STREQ(set->type->name, clash->type->name)
&& set->type->family == clash->type->family
- && set->type->revision == clash->type->revision)
+ && set->type->revision == clash->type->revision
+ && set->variant->same_set(set, clash))
ret = 0;
goto cleanup;
}
@@ -490,7 +631,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
/*
* Finally! Add our shiny new set to the list, and be done.
*/
- D("create: '%s' created with index %u!", set->name, index);
+ pr_debug("create: '%s' created with index %u!", set->name, index);
ip_set_list[index] = set;
return ret;
@@ -509,8 +650,8 @@ out:
static const struct nla_policy
ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = {
[IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 },
- [IPSET_ATTR_SETNAME] = { .type = NLA_STRING,
- .len = IPSET_MAXNAMELEN },
+ [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING,
+ .len = IPSET_MAXNAMELEN - 1 },
};
static inline void
@@ -518,7 +659,7 @@ ip_set_destroy_set(ip_set_id_t index)
{
struct ip_set *set = ip_set_list[index];
- D("set: %s", set->name);
+ pr_debug("set: %s", set->name);
ip_set_list[index] = NULL;
/* Must call it without holding any lock */
@@ -565,7 +706,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
static inline void
ip_set_flush_set(struct ip_set *set)
{
- D("set: %s", set->name);
+ pr_debug("set: %s", set->name);
write_lock_bh(&set->lock);
set->variant->flush(set);
@@ -602,10 +743,10 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
static const struct nla_policy
ip_set_setname2_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = {
[IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 },
- [IPSET_ATTR_SETNAME] = { .type = NLA_STRING,
- .len = IPSET_MAXNAMELEN },
- [IPSET_ATTR_SETNAME2] = { .type = NLA_STRING,
- .len = IPSET_MAXNAMELEN },
+ [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING,
+ .len = IPSET_MAXNAMELEN - 1 },
+ [IPSET_ATTR_SETNAME2] = { .type = NLA_NUL_STRING,
+ .len = IPSET_MAXNAMELEN - 1 },
};
static int
@@ -625,6 +766,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL)
return -EEXIST;
+ if (atomic_read(&set->ref) != 0)
+ return -IPSET_ERR_REFERENCED;
name2 = nla_data(attr[IPSET_ATTR_SETNAME2]);
for (i = 0; i < ip_set_max; i++) {
@@ -638,7 +781,13 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
}
/* Swap two sets so that name/index points to the other.
- * References are also swapped. */
+ * References and set names are also swapped.
+ *
+ * We are protected by the nfnl mutex and references are
+ * manipulated only by holding the mutex. The kernel interfaces
+ * do not hold the mutex but the pointer settings are atomic
+ * so the ip_set_list always contains valid pointers to the sets.
+ */
static int
ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
@@ -648,7 +797,7 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
struct ip_set *from, *to;
ip_set_id_t from_id, to_id;
char from_name[IPSET_MAXNAMELEN];
- uint32_t from_ref;
+ u32 from_ref;
if (unlikely(protocol_failed(attr)
|| attr[IPSET_ATTR_SETNAME] == NULL
@@ -673,7 +822,7 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
&& from->type->family == to->type->family))
return -IPSET_ERR_TYPE_MISMATCH;
- /* No magic here: ref munging protected by the mutex */
+ /* No magic here: ref munging protected by the nfnl_lock */
strncpy(from_name, from->name, IPSET_MAXNAMELEN);
from_ref = atomic_read(&from->ref);
@@ -682,20 +831,29 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
strncpy(to->name, from_name, IPSET_MAXNAMELEN);
atomic_set(&to->ref, from_ref);
- rcu_assign_pointer(ip_set_list[from_id], to);
- rcu_assign_pointer(ip_set_list[to_id], from);
- synchronize_rcu();
+ ip_set_list[from_id] = to;
+ ip_set_list[to_id] = from;
+
+ /* Avoid possible race between ongoing slow add/del in kernel space
+ * and next destroy command. */
+ synchronize_net();
return 0;
}
/* List/save set data */
+#define DUMP_ALL 0L
+#define DUMP_ONE 1L
+#define DUMP_LAST 2L
+
static int
ip_set_dump_done(struct netlink_callback *cb)
{
- if (cb->args[2])
+ if (cb->args[2]) {
+ pr_debug("release set %s", ip_set_list[cb->args[1]]->name);
__ip_set_put((ip_set_id_t) cb->args[1]);
+ }
return 0;
}
@@ -705,9 +863,9 @@ dump_attrs(struct nlmsghdr *nlh)
struct nlattr *attr;
int rem;
- D("dump nlmsg");
+ pr_debug("dump nlmsg");
nlmsg_for_each_attr(attr, nlh, sizeof(struct nfgenmsg), rem) {
- D("type: %u, len %u", nla_type(attr), attr->nla_len);
+ pr_debug("type: %u, len %u", nla_type(attr), attr->nla_len);
}
}
@@ -720,22 +878,32 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0;
int ret = 0;
- max = cb->args[0] ? cb->args[1] + 1 : ip_set_max;
- rcu_read_lock();
+ if (cb->args[1] >= ip_set_max)
+ goto out;
+
+ pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]);
+ max = cb->args[0] == DUMP_ONE ? cb->args[1] + 1 : ip_set_max;
for (; cb->args[1] < max; cb->args[1]++) {
index = (ip_set_id_t) cb->args[1];
- set = rcu_dereference(ip_set_list[index]);
+ set = ip_set_list[index];
if (set == NULL) {
- if (cb->args[0]) {
+ if (cb->args[0] == DUMP_ONE) {
ret = -EEXIST;
- goto unlock;
+ goto out;
}
continue;
}
- D("List set: %s", set->name);
+ /* When dumping all sets, we must dump "sorted"
+ * so that lists (unions of sets) are dumped last.
+ */
+ if (cb->args[0] != DUMP_ONE
+ && !((cb->args[0] == DUMP_ALL)
+ ^ (set->type->features & IPSET_DUMP_LAST)))
+ continue;
+ pr_debug("List set: %s", set->name);
if (!cb->args[2]) {
/* Start listing: make sure set won't be destroyed */
- D("reference set");
+ pr_debug("reference set");
__ip_set_get(index);
}
nlh = start_msg(skb, NETLINK_CB(cb->skb).pid,
@@ -753,7 +921,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
NLA_PUT_STRING(skb, IPSET_ATTR_TYPENAME,
set->type->name);
NLA_PUT_U8(skb, IPSET_ATTR_FAMILY,
- set->type->family);
+ set->family);
NLA_PUT_U8(skb, IPSET_ATTR_REVISION,
set->type->revision);
ret = set->variant->head(set, skb);
@@ -764,28 +932,35 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
read_lock_bh(&set->lock);
ret = set->variant->list(set, skb, cb);
read_unlock_bh(&set->lock);
- if (!cb->args[2])
+ if (!cb->args[2]) {
/* Set is done, proceed with next one */
- cb->args[1]++;
+ if (cb->args[0] == DUMP_ONE)
+ cb->args[1] = IPSET_INVALID_ID;
+ else
+ cb->args[1]++;
+ }
goto release_refcount;
}
}
- goto unlock;
+ goto out;
nla_put_failure:
ret = -EFAULT;
release_refcount:
/* If there was an error or set is done, release set */
if (ret || !cb->args[2]) {
- D("release set");
+ pr_debug("release set %s", ip_set_list[index]->name);
__ip_set_put(index);
}
-unlock:
- rcu_read_unlock();
+ /* If we dump all sets, continue with dumping last ones */
+ if (cb->args[0] == DUMP_ALL && cb->args[1] >= max && !cb->args[2])
+ cb->args[0] = DUMP_LAST;
+
+out:
if (nlh) {
nlmsg_end(skb, nlh);
- D("nlmsg_len: %u", nlh->nlmsg_len);
+ pr_debug("nlmsg_len: %u", nlh->nlmsg_len);
dump_attrs(nlh);
}
@@ -807,23 +982,18 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
ip_set_dump_start,
ip_set_dump_done);
- rcu_read_lock();
- index = find_set_id_rcu(nla_data(attr[IPSET_ATTR_SETNAME]));
- if (index == IPSET_INVALID_ID) {
- rcu_read_unlock();
+ index = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME]));
+ if (index == IPSET_INVALID_ID)
return -EEXIST;
- }
- rcu_read_unlock();
- /* cb->args[0] : 1 => dump single set,
- * : 0 => dump all sets
+ /* cb->args[0] : dump single set/all sets
* [1] : set index
* [..]: type specific
*/
return netlink_dump_init(ctnl, skb, nlh,
ip_set_dump_start,
ip_set_dump_done,
- 2, 1, index);
+ 2, DUMP_ONE, index);
}
/* Add, del and test */
@@ -831,8 +1001,8 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
static const struct nla_policy
ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = {
[IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 },
- [IPSET_ATTR_SETNAME] = { .type = NLA_STRING,
- .len = IPSET_MAXNAMELEN },
+ [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING,
+ .len = IPSET_MAXNAMELEN - 1 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_DATA] = { .type = NLA_NESTED },
[IPSET_ATTR_ADT] = { .type = NLA_NESTED },
@@ -842,11 +1012,11 @@ static int
call_ad(struct sock *ctnl, struct sk_buff *skb,
const struct nlattr * const attr[],
struct ip_set *set, const struct nlattr *nla,
- enum ipset_adt adt, uint32_t flags)
+ enum ipset_adt adt, u32 flags)
{
struct nlattr *head = nla_data(nla);
int ret, len = nla_len(nla), retried = 0;
- uint32_t lineno = 0;
+ u32 lineno = 0;
bool eexist = flags & IPSET_FLAG_EXIST;
do {
@@ -856,13 +1026,13 @@ call_ad(struct sock *ctnl, struct sk_buff *skb,
write_unlock_bh(&set->lock);
} while (ret == -EAGAIN
&& set->variant->resize
- && (ret = set->variant->resize(set, retried++)) == 0);
+ && (ret = set->variant->resize(set, GFP_KERNEL, retried++)) == 0);
if (!ret || (ret == -IPSET_ERR_EXIST && eexist))
return 0;
if (lineno && attr[IPSET_ATTR_LINENO]) {
/* Error in restore/batch mode: send back lineno */
- uint32_t *errline = nla_data(attr[IPSET_ATTR_LINENO]);
+ u32 *errline = nla_data(attr[IPSET_ATTR_LINENO]);
*errline = lineno;
}
@@ -877,7 +1047,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
{
struct ip_set *set;
const struct nlattr *nla;
- uint32_t flags = flag_exist(nlh);
+ u32 flags = flag_exist(nlh);
int ret = 0;
if (unlikely(protocol_failed(attr)
@@ -921,7 +1091,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
{
struct ip_set *set;
const struct nlattr *nla;
- uint32_t flags = flag_exist(nlh);
+ u32 flags = flag_exist(nlh);
int ret = 0;
if (unlikely(protocol_failed(attr)
@@ -1022,7 +1192,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb,
NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL);
NLA_PUT_STRING(skb2, IPSET_ATTR_SETNAME, set->name);
NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, set->type->name);
- NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->type->family);
+ NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->family);
NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->type->revision);
nlmsg_end(skb2, nlh2);
@@ -1044,34 +1214,11 @@ nlmsg_failure:
static const struct nla_policy
ip_set_type_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = {
[IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 },
- [IPSET_ATTR_TYPENAME] = { .type = NLA_STRING,
- .len = IPSET_MAXNAMELEN },
+ [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING,
+ .len = IPSET_MAXNAMELEN - 1 },
[IPSET_ATTR_FAMILY] = { .type = NLA_U8 },
};
-static bool
-find_set_type_minmax(const char *name, uint8_t family,
- uint8_t *min, uint8_t *max)
-{
- struct ip_set_type *type;
- bool ret = false;
-
- *min = *max = 0;
- ip_set_type_list_lock();
- list_for_each_entry(type, &ip_set_type_list, list)
- if (STREQ(type->name, name)
- && (type->family == family || type->family == AF_UNSPEC)) {
- ret = true;
- if (type->revision < *min)
- *min = type->revision;
- else if (type->revision > *max)
- *max = type->revision;
- }
- ip_set_type_list_unlock();
-
- return ret;
-}
-
static int
ip_set_type(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
@@ -1079,7 +1226,7 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb,
{
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
- uint8_t family, min, max;
+ u8 family, min, max;
const char *typename;
int ret = 0;
@@ -1094,7 +1241,7 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb,
/* Try to load in the type module */
load_type_module(typename);
if (!find_set_type_minmax(typename, family, &min, &max)) {
- D("can't find: %s, family: %u", typename, family);
+ pr_debug("can't find: %s, family: %u", typename, family);
return -EEXIST;
}
}
@@ -1114,7 +1261,7 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb,
NLA_PUT_U8(skb2, IPSET_ATTR_REVISION_MIN, min);
nlmsg_end(skb2, nlh2);
- D("Send TYPE, nlmsg_len: %u", nlh2->nlmsg_len);
+ pr_debug("Send TYPE, nlmsg_len: %u", nlh2->nlmsg_len);
ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
if (ret < 0)
return -EFAULT;
@@ -1171,7 +1318,8 @@ nlmsg_failure:
return -EFAULT;
}
-static const struct nfnl_callback ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = {
+static const struct nfnl_callback __read_mostly
+ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = {
[IPSET_CMD_CREATE] = {
.call = ip_set_create,
.attr_count = IPSET_ATTR_CMD_MAX,
@@ -1246,6 +1394,106 @@ static struct nfnetlink_subsystem ip_set_netlink_subsys = {
.cb = ip_set_netlink_subsys_cb,
};
+/* Interface to iptables/ip6tables */
+
+static int
+ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len)
+{
+ unsigned *op;
+ void *data;
+ int copylen = *len, ret = 0;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (optval != SO_IP_SET)
+ return -EBADF;
+ if (*len < sizeof(unsigned)) {
+ return -EINVAL;
+ }
+ data = vmalloc(*len);
+ if (!data)
+ return -ENOMEM;
+ if (copy_from_user(data, user, *len) != 0) {
+ ret = -EFAULT;
+ goto done;
+ }
+ op = (unsigned *) data;
+
+ if (*op < IP_SET_OP_VERSION) {
+ /* Check the version at the beginning of operations */
+ struct ip_set_req_version *req_version = data;
+ if (req_version->version != IPSET_PROTOCOL) {
+ ret = -EPROTO;
+ goto done;
+ }
+ }
+
+ switch (*op) {
+ case IP_SET_OP_VERSION: {
+ struct ip_set_req_version *req_version = data;
+
+ if (*len != sizeof(struct ip_set_req_version)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ req_version->version = IPSET_PROTOCOL;
+ ret = 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 = data;
+
+ if (*len != sizeof(struct ip_set_req_get_set)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
+ nfnl_lock();
+ req_get->set.index = find_set_id(req_get->set.name);
+ nfnl_unlock();
+ goto copy;
+ }
+ case IP_SET_OP_GET_BYINDEX: {
+ struct ip_set_req_get_set *req_get = data;
+
+ if (*len != sizeof(struct ip_set_req_get_set)
+ || req_get->set.index >= ip_set_max) {
+ ret = -EINVAL;
+ goto done;
+ }
+ nfnl_lock();
+ strncpy(req_get->set.name,
+ ip_set_list[req_get->set.index]
+ ? ip_set_list[req_get->set.index]->name : "",
+ IPSET_MAXNAMELEN);
+ nfnl_unlock();
+ goto copy;
+ }
+ default:
+ ret = -EBADMSG;
+ goto done;
+ } /* end of switch(op) */
+
+ copy:
+ ret = copy_to_user(user, data, copylen);
+
+ done:
+ vfree(data);
+ if (ret > 0)
+ ret = 0;
+ return ret;
+}
+
+static struct nf_sockopt_ops so_set = {
+ .pf = PF_INET,
+ .get_optmin = SO_IP_SET,
+ .get_optmax = SO_IP_SET + 1,
+ .get = &ip_set_sockfn_get,
+ .owner = THIS_MODULE,
+};
+
static int __init
ip_set_init(void)
{
@@ -1258,7 +1506,7 @@ ip_set_init(void)
ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL);
if (!ip_set_list) {
- printk(KERN_ERR "Unable to create ip_set_list\n");
+ pr_err("ip_set: Unable to create ip_set_list");
return -ENOMEM;
}
@@ -1266,12 +1514,19 @@ ip_set_init(void)
ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
if (ret != 0) {
- printk("ip_set_init: cannot register with nfnetlink.\n");
+ pr_err("ip_set: cannot register with nfnetlink.");
+ kfree(ip_set_list);
+ return ret;
+ }
+ ret = nf_register_sockopt(&so_set);
+ if (ret != 0) {
+ pr_err("SO_SET registry failed: %d", ret);
+ nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
kfree(ip_set_list);
return ret;
}
- printk("ip_set with protocol version %u loaded\n", IPSET_PROTOCOL);
+ pr_notice("ip_set with protocol version %u loaded", IPSET_PROTOCOL);
return 0;
}
@@ -1279,20 +1534,11 @@ static void __exit
ip_set_fini(void)
{
/* There can't be any existing set */
+ nf_unregister_sockopt(&so_set);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
kfree(ip_set_list);
- D("these are the famous last words");
+ pr_debug("these are the famous last words");
}
-EXPORT_SYMBOL(ip_set_type_register);
-EXPORT_SYMBOL(ip_set_type_unregister);
-
-EXPORT_SYMBOL(ip_set_get_byname);
-EXPORT_SYMBOL(ip_set_put_byindex);
-
-EXPORT_SYMBOL(ip_set_add);
-EXPORT_SYMBOL(ip_set_del);
-EXPORT_SYMBOL(ip_set_test);
-
module_init(ip_set_init);
module_exit(ip_set_fini);
diff --git a/kernel/ip_set_bitmap_ip.c b/kernel/ip_set_bitmap_ip.c
index ccb5473..66d3979 100644
--- a/kernel/ip_set_bitmap_ip.c
+++ b/kernel/ip_set_bitmap_ip.c
@@ -9,6 +9,7 @@
/* Kernel module implementing an IP set type: the bitmap:ip type */
+#include <linux/netfilter/ip_set_kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
@@ -17,7 +18,6 @@
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/netlink.h>
-#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <net/netlink.h>
@@ -38,28 +38,28 @@ MODULE_ALIAS("ip_set_bitmap:ip");
struct bitmap_ip {
void *members; /* the set members */
- uint32_t first_ip; /* host byte order, included in range */
- uint32_t last_ip; /* host byte order, included in range */
- uint32_t elements; /* number of max elements in the set */
- uint32_t hosts; /* number of hosts in a subnet */
+ u32 first_ip; /* host byte order, included in range */
+ u32 last_ip; /* host byte order, included in range */
+ u32 elements; /* number of max elements in the set */
+ u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */
- uint8_t netmask; /* subnet netmask */
+ u8 netmask; /* subnet netmask */
};
-static inline uint32_t
-ip_to_id(const struct bitmap_ip *map, uint32_t ip)
+static inline u32
+ip_to_id(const struct bitmap_ip *map, u32 ip)
{
return ((ip & HOSTMASK(map->netmask)) - map->first_ip)/map->hosts;
}
static inline int
-bitmap_ip_test(const struct bitmap_ip *map, uint32_t id)
+bitmap_ip_test(const struct bitmap_ip *map, u32 id)
{
return !!test_bit(id, map->members);
}
static inline int
-bitmap_ip_add(struct bitmap_ip *map, uint32_t id)
+bitmap_ip_add(struct bitmap_ip *map, u32 id)
{
if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST;
@@ -68,7 +68,7 @@ bitmap_ip_add(struct bitmap_ip *map, uint32_t id)
}
static inline int
-bitmap_ip_del(struct bitmap_ip *map, uint32_t id)
+bitmap_ip_del(struct bitmap_ip *map, u32 id)
{
if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST;
@@ -78,14 +78,12 @@ bitmap_ip_del(struct bitmap_ip *map, uint32_t id)
static int
bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags)
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_ip *map = set->data;
- uint32_t ip = ntohl(ip4addr(skb, flags));
+ u32 ip;
- if (pf != AF_INET)
- return -EINVAL;
-
+ ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
@@ -113,12 +111,12 @@ bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = {
static int
bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct bitmap_ip *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
bool eexist = flags & IPSET_FLAG_EXIST;
- uint32_t ip, ip_to, id;
+ u32 ip, ip_to, id;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
@@ -133,6 +131,8 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
+ /* Set was defined without timeout support,
+ * don't ignore attribute silently */
if (tb[IPSET_ATTR_TIMEOUT])
return -IPSET_ERR_TIMEOUT;
@@ -147,10 +147,11 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
return -IPSET_ERR_BITMAP_RANGE;
}
} else if (tb[IPSET_ATTR_CIDR]) {
- uint8_t cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+ u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
+ ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
@@ -168,7 +169,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
return ret;
}
- };
+ }
return ret;
}
@@ -196,11 +197,6 @@ bitmap_ip_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ip *map = set->data;
struct nlattr *nested;
- uint32_t id, elements;
-
- for (id = 0, elements = 0; id < map->elements; id++)
- if (bitmap_ip_test(map, id))
- elements++;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
@@ -209,10 +205,10 @@ bitmap_ip_head(struct ip_set *set, struct sk_buff *skb)
NLA_PUT_NET32(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
if (map->netmask != 32)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask);
- NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
- NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(map->memsize));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
+ htonl(sizeof(*map) + map->memsize));
ipset_nest_end(skb, nested);
return 0;
@@ -226,7 +222,7 @@ bitmap_ip_list(struct ip_set *set,
{
const struct bitmap_ip *map = set->data;
struct nlattr *atd, *nested;
- uint32_t id, first = cb->args[2];
+ u32 id, first = cb->args[2];
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
@@ -245,8 +241,6 @@ bitmap_ip_list(struct ip_set *set,
}
NLA_PUT_NET32(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts));
- if (map->netmask != 32)
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR, map->netmask);
ipset_nest_end(skb, nested);
}
ipset_nest_end(skb, atd);
@@ -260,6 +254,17 @@ nla_put_failure:
return 0;
}
+static bool
+bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
+{
+ struct bitmap_ip *x = a->data;
+ struct bitmap_ip *y = b->data;
+
+ return x->first_ip == y->first_ip
+ && x->last_ip == y->last_ip
+ && x->netmask == y->netmask;
+}
+
static const struct ip_set_type_variant bitmap_ip __read_mostly = {
.kadt = bitmap_ip_kadt,
.uadt = bitmap_ip_uadt,
@@ -267,34 +272,35 @@ static const struct ip_set_type_variant bitmap_ip __read_mostly = {
.flush = bitmap_ip_flush,
.head = bitmap_ip_head,
.list = bitmap_ip_list,
+ .same_set = bitmap_ip_same_set,
};
/* Timeout variant */
struct bitmap_ip_timeout {
void *members; /* the set members */
- uint32_t first_ip; /* host byte order, included in range */
- uint32_t last_ip; /* host byte order, included in range */
- uint32_t elements; /* number of max elements in the set */
- uint32_t hosts; /* number of hosts in a subnet */
+ u32 first_ip; /* host byte order, included in range */
+ u32 last_ip; /* host byte order, included in range */
+ u32 elements; /* number of max elements in the set */
+ u32 hosts; /* number of hosts in a subnet */
size_t memsize; /* members size */
- uint8_t netmask; /* subnet netmask */
+ u8 netmask; /* subnet netmask */
- uint32_t timeout; /* timeout parameter */
+ u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
static inline bool
-bitmap_ip_timeout_test(const struct bitmap_ip_timeout *map, uint32_t id)
+bitmap_ip_timeout_test(const struct bitmap_ip_timeout *map, u32 id)
{
unsigned long *table = map->members;
return ip_set_timeout_test(table[id]);
}
-static int
+static inline int
bitmap_ip_timeout_add(struct bitmap_ip_timeout *map,
- uint32_t id, uint32_t timeout)
+ u32 id, u32 timeout)
{
unsigned long *table = map->members;
@@ -306,8 +312,8 @@ bitmap_ip_timeout_add(struct bitmap_ip_timeout *map,
return 0;
}
-static int
-bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, uint32_t id)
+static inline int
+bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, u32 id)
{
unsigned long *table = map->members;
int ret = -IPSET_ERR_EXIST;
@@ -321,14 +327,12 @@ bitmap_ip_timeout_del(struct bitmap_ip_timeout *map, uint32_t id)
static int
bitmap_ip_timeout_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags)
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_ip_timeout *map = set->data;
- uint32_t ip = ntohl(ip4addr(skb, flags));
-
- if (pf != AF_INET)
- return -EINVAL;
+ u32 ip;
+ ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
@@ -348,12 +352,12 @@ bitmap_ip_timeout_kadt(struct ip_set *set, const struct sk_buff *skb,
static int
bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct bitmap_ip_timeout *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
bool eexist = flags & IPSET_FLAG_EXIST;
- uint32_t ip, ip_to, id, timeout = map->timeout;
+ u32 ip, ip_to, id, timeout = map->timeout;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
@@ -380,10 +384,11 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
return -IPSET_ERR_BITMAP_RANGE;
}
} else if (tb[IPSET_ATTR_CIDR]) {
- uint8_t cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+ u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
+ ip &= HOSTMASK(cidr);
ip_to = ip | ~HOSTMASK(cidr);
} else
ip_to = ip;
@@ -391,9 +396,8 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
if (ip_to > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
- if (tb[IPSET_ATTR_TIMEOUT]) {
- timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
- }
+ if (tb[IPSET_ATTR_TIMEOUT])
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
for (; !before(ip_to, ip); ip += map->hosts) {
id = ip_to_id((const struct bitmap_ip *)map, ip);
@@ -414,11 +418,8 @@ static void
bitmap_ip_timeout_destroy(struct ip_set *set)
{
struct bitmap_ip_timeout *map = set->data;
-
- /* gc might be running: del_timer_sync can't be used */
- while (!del_timer(&map->gc))
- msleep(IPSET_DESTROY_TIMER_SLEEP);
-
+
+ del_timer_sync(&map->gc);
ip_set_free(map->members, set->flags);
kfree(map);
@@ -438,11 +439,6 @@ bitmap_ip_timeout_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ip_timeout *map = set->data;
struct nlattr *nested;
- uint32_t id, elements;
-
- for (id = 0, elements = 0; id < map->elements; id++)
- if (bitmap_ip_timeout_test(map, id))
- elements++;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
@@ -452,10 +448,10 @@ bitmap_ip_timeout_head(struct ip_set *set, struct sk_buff *skb)
if (map->netmask != 32)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT , htonl(map->timeout));
- NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
- NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(map->memsize));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
+ htonl(sizeof(*map) + map->memsize));
ipset_nest_end(skb, nested);
return 0;
@@ -469,7 +465,7 @@ bitmap_ip_timeout_list(struct ip_set *set,
{
const struct bitmap_ip_timeout *map = set->data;
struct nlattr *adt, *nested;
- uint32_t id, first = cb->args[2];
+ u32 id, first = cb->args[2];
unsigned long *table = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
@@ -489,8 +485,6 @@ bitmap_ip_timeout_list(struct ip_set *set,
}
NLA_PUT_NET32(skb, IPSET_ATTR_IP,
htonl(map->first_ip + id * map->hosts));
- if (map->netmask != 32)
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR, map->netmask);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(table[id])));
ipset_nest_end(skb, nested);
@@ -508,6 +502,18 @@ nla_put_failure:
return 0;
}
+static bool
+bitmap_ip_timeout_same_set(const struct ip_set *a, const struct ip_set *b)
+{
+ struct bitmap_ip_timeout *x = a->data;
+ struct bitmap_ip_timeout *y = b->data;
+
+ return x->first_ip == y->first_ip
+ && x->last_ip == y->last_ip
+ && x->netmask == y->netmask
+ && x->timeout == y->timeout;
+}
+
static const struct ip_set_type_variant bitmap_ip_timeout __read_mostly = {
.kadt = bitmap_ip_timeout_kadt,
.uadt = bitmap_ip_timeout_uadt,
@@ -515,15 +521,16 @@ static const struct ip_set_type_variant bitmap_ip_timeout __read_mostly = {
.flush = bitmap_ip_timeout_flush,
.head = bitmap_ip_timeout_head,
.list = bitmap_ip_timeout_list,
+ .same_set = bitmap_ip_timeout_same_set,
};
static void
-bitmap_ip_timeout_gc(unsigned long ul_set)
+bitmap_ip_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_ip_timeout *map = set->data;
unsigned long *table = map->members;
- uint32_t id;
+ u32 id;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
@@ -544,7 +551,7 @@ bitmap_ip_gc_init(struct ip_set *set)
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
- map->gc.function = bitmap_ip_timeout_gc;
+ map->gc.function = bitmap_ip_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
@@ -562,8 +569,8 @@ bitmap_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
static bool
init_map_ip(struct ip_set *set, struct bitmap_ip *map,
- uint32_t first_ip, uint32_t last_ip,
- uint32_t elements, uint32_t hosts, uint8_t netmask)
+ u32 first_ip, u32 last_ip,
+ u32 elements, u32 hosts, u8 netmask)
{
map->members = ip_set_alloc(map->memsize, GFP_KERNEL, &set->flags);
if (!map->members)
@@ -582,11 +589,11 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
static int
bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
- uint32_t flags)
+ u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
- uint32_t first_ip, last_ip, hosts, elements;
- uint8_t netmask = 32;
+ u32 first_ip, last_ip, hosts, elements;
+ u8 netmask = 32;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_ip_create_policy))
@@ -600,13 +607,13 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_IP_TO]) {
last_ip = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]);
if (first_ip > last_ip) {
- uint32_t tmp = first_ip;
+ u32 tmp = first_ip;
first_ip = last_ip;
last_ip = tmp;
}
} else if (tb[IPSET_ATTR_CIDR]) {
- uint8_t cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+ u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR;
@@ -628,8 +635,8 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
hosts = 1;
elements = last_ip - first_ip + 1;
} else {
- uint8_t mask_bits;
- uint32_t mask;
+ u8 mask_bits;
+ u32 mask;
mask = range_to_mask(first_ip, last_ip, &mask_bits);
@@ -637,14 +644,14 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
|| netmask <= mask_bits)
return -IPSET_ERR_BITMAP_RANGE;
- D("mask_bits %u, netmask %u", mask_bits, netmask);
+ pr_debug("mask_bits %u, netmask %u", mask_bits, netmask);
hosts = 2 << (32 - netmask - 1);
elements = 2 << (netmask - mask_bits - 1);
}
if (elements > IPSET_BITMAP_MAX_RANGE + 1) {
return -IPSET_ERR_BITMAP_RANGE_SIZE;
}
- D("hosts %u, elements %u", hosts, elements);
+ pr_debug("hosts %u, elements %u", hosts, elements);
if (tb[IPSET_ATTR_TIMEOUT]) {
struct bitmap_ip_timeout *map;
@@ -662,8 +669,7 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *head, int len,
return -ENOMEM;
}
- map->timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
- set->flags |= IP_SET_FLAG_TIMEOUT;
+ map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_ip_timeout;
bitmap_ip_gc_init(set);
@@ -692,6 +698,7 @@ static struct ip_set_type bitmap_ip_type = {
.name = "bitmap:ip",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP,
+ .dimension = IPSET_DIM_ONE,
.family = AF_INET,
.revision = 0,
.create = bitmap_ip_create,
diff --git a/kernel/ip_set_bitmap_ipmac.c b/kernel/ip_set_bitmap_ipmac.c
index 45335dd..d036862 100644
--- a/kernel/ip_set_bitmap_ipmac.c
+++ b/kernel/ip_set_bitmap_ipmac.c
@@ -10,8 +10,10 @@
/* Kernel module implementing an IP set type: the bitmap:ip,mac type */
+#include <linux/netfilter/ip_set_kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
+#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
@@ -19,7 +21,6 @@
#include <linux/spinlock.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
-#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <net/netlink.h>
@@ -40,119 +41,235 @@ enum {
MAC_UNSET, /* element is set, without MAC */
};
-/* Member element without and with timeout */
+/* Type structure */
+struct bitmap_ipmac {
+ void *members; /* the set members */
+ u32 first_ip; /* host byte order, included in range */
+ u32 last_ip; /* host byte order, included in range */
+ u32 timeout; /* timeout value */
+ struct timer_list gc; /* garbage collector */
+ size_t dsize; /* size of element */
+};
struct ipmac {
+ u32 id; /* id in array */
+ unsigned char *ether; /* ethernet address */
+};
+
+/* Member element without and with timeout */
+
+struct ipmac_elem {
unsigned char ether[ETH_ALEN];
unsigned char match;
};
-struct ipmac_timeout {
+struct ipmac_telem {
unsigned char ether[ETH_ALEN];
unsigned char match;
unsigned long timeout;
};
-struct bitmap_ipmac {
- void *members; /* the set members */
- uint32_t first_ip; /* host byte order, included in range */
- uint32_t last_ip; /* host byte order, included in range */
- uint32_t timeout; /* timeout value */
- struct timer_list gc; /* garbage collector */
- size_t elem_size; /* size of element */
-};
-
static inline void *
-bitmap_ipmac_elem(const struct bitmap_ipmac *map, uint32_t id)
+bitmap_ipmac_elem(const struct bitmap_ipmac *map, u32 id)
{
- return (void *)((char *)map->members + id * map->elem_size);
+ return (void *)((char *)map->members + id * map->dsize);
}
static inline bool
-bitmap_timeout(const struct bitmap_ipmac *map, uint32_t id)
+bitmap_timeout(const struct bitmap_ipmac *map, u32 id)
{
- const struct ipmac_timeout *elem = bitmap_ipmac_elem(map, id);
+ const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id);
return ip_set_timeout_test(elem->timeout);
}
static inline bool
-bitmap_expired(const struct bitmap_ipmac *map, uint32_t id)
+bitmap_expired(const struct bitmap_ipmac *map, u32 id)
{
- const struct ipmac_timeout *elem = bitmap_ipmac_elem(map, id);
+ const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id);
return ip_set_timeout_expired(elem->timeout);
}
static inline int
-bitmap_ipmac_exist(const struct ipmac *elem, bool with_timeout)
+bitmap_ipmac_exist(const struct ipmac_telem *elem)
{
- const struct ipmac_timeout *e = (const struct ipmac_timeout *) elem;
-
return elem->match == MAC_UNSET
|| (elem->match == MAC_FILLED
- && !(with_timeout && ip_set_timeout_expired(e->timeout)));
+ && !ip_set_timeout_expired(elem->timeout));
}
-static inline int
-bitmap_ipmac_test(const struct bitmap_ipmac *map, bool with_timeout,
- uint32_t id, const unsigned char *ether)
+/* Base variant */
+
+static int
+bitmap_ipmac_test(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
{
- const struct ipmac *elem = bitmap_ipmac_elem(map, id);
+ const struct bitmap_ipmac *map = set->data;
+ const struct ipmac *data = value;
+ const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
switch (elem->match) {
case MAC_UNSET:
/* Trigger kernel to fill out the ethernet address */
return -EAGAIN;
case MAC_FILLED:
- return (ether == NULL
- || memcmp(ether, elem->ether, ETH_ALEN) == 0)
- && (!with_timeout || bitmap_timeout(map, id));
+ return data->ether == NULL
+ || compare_ether_addr(data->ether, elem->ether) == 0;
}
return 0;
}
static int
-bitmap_ipmac_add(struct bitmap_ipmac *map, bool with_timeout,
- uint32_t id, const unsigned char *ether,
- uint32_t timeout)
+bitmap_ipmac_add(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
{
- struct ipmac *elem = bitmap_ipmac_elem(map, id);
- struct ipmac_timeout *e = (struct ipmac_timeout *) elem;
+ struct bitmap_ipmac *map = set->data;
+ const struct ipmac *data = value;
+ struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
switch (elem->match) {
case MAC_UNSET:
- if (!ether)
+ if (!data->ether)
/* Already added without ethernet address */
return -IPSET_ERR_EXIST;
/* Fill the MAC address and activate the timer */
- memcpy(elem->ether, ether, ETH_ALEN);
+ memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
- if (with_timeout) {
- if (timeout == map->timeout)
- /* Timeout was not specified, get stored one */
- timeout = e->timeout;
- e->timeout = ip_set_timeout_set(timeout);
+ break;
+ case MAC_FILLED:
+ return -IPSET_ERR_EXIST;
+ case MAC_EMPTY:
+ if (data->ether) {
+ memcpy(elem->ether, data->ether, ETH_ALEN);
+ elem->match = MAC_FILLED;
+ } else
+ elem->match = MAC_UNSET;
+ }
+
+ return 0;
+}
+
+static int
+bitmap_ipmac_del(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct bitmap_ipmac *map = set->data;
+ const struct ipmac *data = value;
+ struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
+
+ if (elem->match == MAC_EMPTY)
+ return -IPSET_ERR_EXIST;
+
+ elem->match = MAC_EMPTY;
+
+ return 0;
+}
+
+static int
+bitmap_ipmac_list(struct ip_set *set,
+ struct sk_buff *skb, struct netlink_callback *cb)
+{
+ const struct bitmap_ipmac *map = set->data;
+ const struct ipmac_elem *elem;
+ struct nlattr *atd, *nested;
+ u32 id, first = cb->args[2];
+ u32 last = map->last_ip - map->first_ip;
+
+ atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
+ if (!atd)
+ return -EFAULT;
+ for (; cb->args[2] <= last; cb->args[2]++) {
+ id = cb->args[2];
+ elem = bitmap_ipmac_elem(map, id);
+ if (elem->match == MAC_EMPTY)
+ continue;
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested) {
+ if (id == first) {
+ nla_nest_cancel(skb, atd);
+ return -EFAULT;
+ } else
+ goto nla_put_failure;
}
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP,
+ htonl(map->first_ip + id));
+ if (elem->match == MAC_FILLED)
+ NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN,
+ elem->ether);
+ ipset_nest_end(skb, nested);
+ }
+ ipset_nest_end(skb, atd);
+ /* Set listing finished */
+ cb->args[2] = 0;
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nested);
+ ipset_nest_end(skb, atd);
+ return 0;
+}
+
+/* Timeout variant */
+
+static int
+bitmap_ipmac_ttest(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ const struct bitmap_ipmac *map = set->data;
+ const struct ipmac *data = value;
+ const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
+
+ switch (elem->match) {
+ case MAC_UNSET:
+ /* Trigger kernel to fill out the ethernet address */
+ return -EAGAIN;
+ case MAC_FILLED:
+ return (data->ether == NULL
+ || compare_ether_addr(data->ether, elem->ether) == 0)
+ && !bitmap_expired(map, data->id);
+ }
+ return 0;
+}
+
+static int
+bitmap_ipmac_tadd(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct bitmap_ipmac *map = set->data;
+ const struct ipmac *data = value;
+ struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
+
+ switch (elem->match) {
+ case MAC_UNSET:
+ if (!data->ether)
+ /* Already added without ethernet address */
+ return -IPSET_ERR_EXIST;
+ /* Fill the MAC address and activate the timer */
+ memcpy(elem->ether, data->ether, ETH_ALEN);
+ elem->match = MAC_FILLED;
+ if (timeout == map->timeout)
+ /* Timeout was not specified, get stored one */
+ timeout = elem->timeout;
+ elem->timeout = ip_set_timeout_set(timeout);
break;
case MAC_FILLED:
- if (!(with_timeout && bitmap_expired(map, id)))
+ if (bitmap_expired(map, data->id))
return -IPSET_ERR_EXIST;
/* Fall through */
case MAC_EMPTY:
- if (ether) {
- memcpy(elem->ether, ether, ETH_ALEN);
+ if (data->ether) {
+ memcpy(elem->ether, data->ether, ETH_ALEN);
elem->match = MAC_FILLED;
} else
elem->match = MAC_UNSET;
- if (with_timeout) {
- /* If MAC is unset yet, we store plain timeout
- * because the timer is not activated yet
- * and we can reuse it later when MAC is filled out,
- * possibly by the kernel */
- e->timeout = ether ? ip_set_timeout_set(timeout)
- : timeout;
- }
+ /* If MAC is unset yet, we store plain timeout
+ * because the timer is not activated yet
+ * and we can reuse it later when MAC is filled out,
+ * possibly by the kernel */
+ elem->timeout = data->ether ? ip_set_timeout_set(timeout)
+ : timeout;
break;
}
@@ -160,13 +277,14 @@ bitmap_ipmac_add(struct bitmap_ipmac *map, bool with_timeout,
}
static int
-bitmap_ipmac_del(struct bitmap_ipmac *map, bool with_timeout,
- uint32_t id)
+bitmap_ipmac_tdel(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
{
- struct ipmac *elem = bitmap_ipmac_elem(map, id);
+ struct bitmap_ipmac *map = set->data;
+ const struct ipmac *data = value;
+ struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
- if (elem->match == MAC_EMPTY
- || (with_timeout && bitmap_expired(map, id)))
+ if (elem->match == MAC_EMPTY || bitmap_expired(map, data->id))
return -IPSET_ERR_EXIST;
elem->match = MAC_EMPTY;
@@ -175,38 +293,74 @@ bitmap_ipmac_del(struct bitmap_ipmac *map, bool with_timeout,
}
static int
+bitmap_ipmac_tlist(struct ip_set *set,
+ struct sk_buff *skb, struct netlink_callback *cb)
+{
+ const struct bitmap_ipmac *map = set->data;
+ const struct ipmac_telem *elem;
+ struct nlattr *atd, *nested;
+ u32 id, first = cb->args[2];
+ u32 timeout, last = map->last_ip - map->first_ip;
+
+ atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
+ if (!atd)
+ return -EFAULT;
+ for (; cb->args[2] <= last; cb->args[2]++) {
+ id = cb->args[2];
+ elem = bitmap_ipmac_elem(map, id);
+ if (!bitmap_ipmac_exist(elem))
+ continue;
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested) {
+ if (id == first) {
+ nla_nest_cancel(skb, atd);
+ return -EFAULT;
+ } else
+ goto nla_put_failure;
+ }
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP,
+ htonl(map->first_ip + id));
+ if (elem->match == MAC_FILLED)
+ NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN,
+ elem->ether);
+ timeout = elem->match == MAC_UNSET ? elem->timeout
+ : ip_set_timeout_get(elem->timeout);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(timeout));
+ ipset_nest_end(skb, nested);
+ }
+ ipset_nest_end(skb, atd);
+ /* Set listing finished */
+ cb->args[2] = 0;
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nested);
+ ipset_nest_end(skb, atd);
+ return 0;
+}
+
+static int
bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags)
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_ipmac *map = set->data;
- uint32_t ip = ntohl(ip4addr(skb, flags));
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
-
- if (pf != AF_INET)
- return -EINVAL;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct ipmac data;
- if (ip < map->first_ip || ip > map->last_ip)
+ data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
+ if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
+ /* Backward compatibility: we don't check the second flag */
if (skb_mac_header(skb) < skb->head
|| (skb_mac_header(skb) + ETH_HLEN) > skb->data)
return -EINVAL;
- ip -= map->first_ip;
-
- switch (adt) {
- case IPSET_TEST:
- return bitmap_ipmac_test(map, with_timeout,
- ip, eth_hdr(skb)->h_source);
- case IPSET_ADD:
- return bitmap_ipmac_add(map, with_timeout,
- ip, eth_hdr(skb)->h_source,
- map->timeout);
- case IPSET_DEL:
- return bitmap_ipmac_del(map, with_timeout, ip);
- default:
- return -EINVAL;
- }
+ data.id -= map->first_ip;
+ data.ether = eth_hdr(skb)->h_source;
+
+ return adtfn(set, &data, GFP_ATOMIC, map->timeout);
}
static const struct nla_policy
@@ -218,14 +372,14 @@ bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
static int
bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct bitmap_ipmac *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ ipset_adtfn adtfn = set->variant->adt[adt];
bool eexist = flags & IPSET_FLAG_EXIST;
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
- uint32_t ip, timeout = map->timeout;
- unsigned char *ether = NULL;
+ struct ipmac data;
+ u32 timeout = map->timeout;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
@@ -233,35 +387,31 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_IP])
- ip = ip_set_get_h32(tb[IPSET_ATTR_IP]);
+ data.id = ip_set_get_h32(tb[IPSET_ATTR_IP]);
else
return -IPSET_ERR_PROTOCOL;
- if (ip < map->first_ip || ip > map->last_ip)
+ if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_ETHER])
- ether = nla_data(tb[IPSET_ATTR_ETHER]);
+ data.ether = nla_data(tb[IPSET_ATTR_ETHER]);
+ else
+ data.ether = NULL;
if (tb[IPSET_ATTR_TIMEOUT]) {
- if (!with_timeout)
+ if (!with_timeout(map->timeout))
return -IPSET_ERR_TIMEOUT;
- timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- ip -= map->first_ip;
-
- if (adt == IPSET_TEST)
- return bitmap_ipmac_test(map, with_timeout, ip, ether);
+ data.id -= map->first_ip;
- ret = adt == IPSET_ADD ? bitmap_ipmac_add(map, with_timeout,
- ip, ether, timeout)
- : bitmap_ipmac_del(map, with_timeout, ip);
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
if (tb[IPSET_ATTR_LINENO])
*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- return ret;
}
return ret;
}
@@ -271,10 +421,8 @@ bitmap_ipmac_destroy(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
- /* gc might be running: del_timer_sync can't be used */
- if (set->flags & IP_SET_FLAG_TIMEOUT)
- while (!del_timer(&map->gc))
- msleep(IPSET_DESTROY_TIMER_SLEEP);
+ if (with_timeout(map->timeout))
+ del_timer_sync(&map->gc);
ip_set_free(map->members, set->flags);
kfree(map);
@@ -288,7 +436,7 @@ bitmap_ipmac_flush(struct ip_set *set)
struct bitmap_ipmac *map = set->data;
memset(map->members, 0,
- (map->last_ip - map->first_ip + 1) * map->elem_size);
+ (map->last_ip - map->first_ip + 1) * map->dsize);
}
static int
@@ -296,28 +444,18 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb)
{
const struct bitmap_ipmac *map = set->data;
struct nlattr *nested;
- const struct ipmac *elem;
- uint32_t id, elements = 0, last = map->last_ip - map->first_ip;
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
-
- for (id = 0; id <= last; id++) {
- elem = bitmap_ipmac_elem(map, id);
- if (bitmap_ipmac_exist(elem, with_timeout))
- elements++;
- }
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_NET32(skb, IPSET_ATTR_IP, htonl(map->first_ip));
NLA_PUT_NET32(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
- NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
- htonl((map->last_ip - map->first_ip + 1)
- * map->elem_size));
- if (with_timeout)
+ htonl(sizeof(*map)
+ + (map->last_ip - map->first_ip + 1) * map->dsize));
+ if (with_timeout(map->timeout))
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
ipset_nest_end(skb, nested);
@@ -326,77 +464,54 @@ nla_put_failure:
return -EFAULT;
}
-static int
-bitmap_ipmac_list(struct ip_set *set,
- struct sk_buff *skb, struct netlink_callback *cb)
+static bool
+bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
{
- const struct bitmap_ipmac *map = set->data;
- const struct ipmac *elem;
- struct nlattr *atd, *nested;
- uint32_t id, first = cb->args[2];
- uint32_t last = map->last_ip - map->first_ip;
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
-
- atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
- if (!atd)
- return -EFAULT;
- for (; cb->args[2] <= last; cb->args[2]++) {
- id = cb->args[2];
- elem = bitmap_ipmac_elem(map, id);
- if (!bitmap_ipmac_exist(elem, with_timeout))
- continue;
- nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
- if (!nested) {
- if (id == first) {
- nla_nest_cancel(skb, atd);
- return -EFAULT;
- } else
- goto nla_put_failure;
- }
- NLA_PUT_NET32(skb, IPSET_ATTR_IP,
- htonl(map->first_ip + id));
- if (elem->match == MAC_FILLED)
- NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN,
- elem->ether);
- if (with_timeout) {
- const struct ipmac_timeout *e =
- (const struct ipmac_timeout *)elem;
- uint32_t timeout = e->match == MAC_UNSET ? e->timeout
- : ip_set_timeout_get(e->timeout);
+ struct bitmap_ipmac *x = a->data;
+ struct bitmap_ipmac *y = b->data;
- NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
- htonl(timeout));
- }
- ipset_nest_end(skb, nested);
- }
- ipset_nest_end(skb, atd);
- /* Set listing finished */
- cb->args[2] = 0;
-
- return 0;
-
-nla_put_failure:
- nla_nest_cancel(skb, nested);
- ipset_nest_end(skb, atd);
- return 0;
+ return x->first_ip == y->first_ip
+ && x->last_ip == y->last_ip
+ && x->timeout == y->timeout;
}
const struct ip_set_type_variant bitmap_ipmac __read_mostly = {
.kadt = bitmap_ipmac_kadt,
.uadt = bitmap_ipmac_uadt,
+ .adt = {
+ [IPSET_ADD] = bitmap_ipmac_add,
+ [IPSET_DEL] = bitmap_ipmac_del,
+ [IPSET_TEST] = bitmap_ipmac_test,
+ },
.destroy = bitmap_ipmac_destroy,
.flush = bitmap_ipmac_flush,
.head = bitmap_ipmac_head,
.list = bitmap_ipmac_list,
+ .same_set = bitmap_ipmac_same_set,
+};
+
+const struct ip_set_type_variant bitmap_tipmac __read_mostly = {
+ .kadt = bitmap_ipmac_kadt,
+ .uadt = bitmap_ipmac_uadt,
+ .adt = {
+ [IPSET_ADD] = bitmap_ipmac_tadd,
+ [IPSET_DEL] = bitmap_ipmac_tdel,
+ [IPSET_TEST] = bitmap_ipmac_ttest,
+ },
+ .destroy = bitmap_ipmac_destroy,
+ .flush = bitmap_ipmac_flush,
+ .head = bitmap_ipmac_head,
+ .list = bitmap_ipmac_tlist,
+ .same_set = bitmap_ipmac_same_set,
};
static void
-bitmap_ipmac_timeout_gc(unsigned long ul_set)
+bitmap_ipmac_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_ipmac *map = set->data;
- struct ipmac_timeout *elem;
- uint32_t id, last = map->last_ip - map->first_ip;
+ struct ipmac_telem *elem;
+ u32 id, last = map->last_ip - map->first_ip;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
@@ -414,13 +529,13 @@ bitmap_ipmac_timeout_gc(unsigned long ul_set)
}
static inline void
-bitmap_ipmac_timeout_gc_init(struct ip_set *set)
+bitmap_ipmac_gc_init(struct ip_set *set)
{
struct bitmap_ipmac *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
- map->gc.function = bitmap_ipmac_timeout_gc;
+ map->gc.function = bitmap_ipmac_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
@@ -436,14 +551,15 @@ bitmap_ipmac_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
static bool
init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
- uint32_t first_ip, uint32_t last_ip)
+ u32 first_ip, u32 last_ip)
{
- map->members = ip_set_alloc((last_ip - first_ip + 1) * map->elem_size,
+ map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize,
GFP_KERNEL, &set->flags);
if (!map->members)
return false;
map->first_ip = first_ip;
map->last_ip = last_ip;
+ map->timeout = IPSET_NO_TIMEOUT;
set->data = map;
set->family = AF_INET;
@@ -453,10 +569,10 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
static int
bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
- uint32_t flags)
+ u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
- uint32_t first_ip, last_ip, elements;
+ u32 first_ip, last_ip, elements;
struct bitmap_ipmac *map;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
@@ -471,13 +587,13 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_IP_TO]) {
last_ip = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]);
if (first_ip > last_ip) {
- uint32_t tmp = first_ip;
+ u32 tmp = first_ip;
first_ip = last_ip;
last_ip = tmp;
}
} else if (tb[IPSET_ATTR_CIDR]) {
- uint8_t cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+ u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
if (cidr >= 32)
return -IPSET_ERR_INVALID_CIDR;
@@ -490,31 +606,32 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
if (elements > IPSET_BITMAP_MAX_RANGE + 1)
return -IPSET_ERR_BITMAP_RANGE_SIZE;
- set->variant = &bitmap_ipmac;
-
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
- map->elem_size = sizeof(struct ipmac_timeout);
+ map->dsize = sizeof(struct ipmac_telem);
if (!init_map_ipmac(set, map, first_ip, last_ip)) {
kfree(map);
return -ENOMEM;
}
- map->timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
- set->flags |= IP_SET_FLAG_TIMEOUT;
+ map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- bitmap_ipmac_timeout_gc_init(set);
+ set->variant = &bitmap_tipmac;
+
+ bitmap_ipmac_gc_init(set);
} else {
- map->elem_size = sizeof(struct ipmac);
+ map->dsize = sizeof(struct ipmac_elem);
if (!init_map_ipmac(set, map, first_ip, last_ip)) {
kfree(map);
return -ENOMEM;
}
+ set->variant = &bitmap_ipmac;
+
}
return 0;
}
@@ -522,7 +639,8 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *head, int len,
struct ip_set_type bitmap_ipmac_type = {
.name = "bitmap:ip,mac",
.protocol = IPSET_PROTOCOL,
- .features = IPSET_TYPE_IP,
+ .features = IPSET_TYPE_IP | IPSET_TYPE_MAC,
+ .dimension = IPSET_DIM_TWO,
.family = AF_INET,
.revision = 0,
.create = bitmap_ipmac_create,
diff --git a/kernel/ip_set_bitmap_port.c b/kernel/ip_set_bitmap_port.c
index 3afd031..f3e498a 100644
--- a/kernel/ip_set_bitmap_port.c
+++ b/kernel/ip_set_bitmap_port.c
@@ -7,6 +7,7 @@
/* Kernel module implementing an IP set type: the bitmap:port type */
+#include <linux/netfilter/ip_set_kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/tcp.h>
@@ -17,7 +18,6 @@
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/netlink.h>
-#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <net/netlink.h>
@@ -38,19 +38,19 @@ MODULE_ALIAS("ip_set_bitmap:port");
struct bitmap_port {
void *members; /* the set members */
- uint16_t first_port; /* host byte order, included in range */
- uint16_t last_port; /* host byte order, included in range */
+ u16 first_port; /* host byte order, included in range */
+ u16 last_port; /* host byte order, included in range */
size_t memsize; /* members size */
};
static inline int
-bitmap_port_test(const struct bitmap_port *map, uint16_t id)
+bitmap_port_test(const struct bitmap_port *map, u16 id)
{
return !!test_bit(id, map->members);
}
static inline int
-bitmap_port_add(struct bitmap_port *map, uint16_t id)
+bitmap_port_add(struct bitmap_port *map, u16 id)
{
if (test_and_set_bit(id, map->members))
return -IPSET_ERR_EXIST;
@@ -59,7 +59,7 @@ bitmap_port_add(struct bitmap_port *map, uint16_t id)
}
static int
-bitmap_port_del(struct bitmap_port *map, uint16_t id)
+bitmap_port_del(struct bitmap_port *map, u16 id)
{
if (!test_and_clear_bit(id, map->members))
return -IPSET_ERR_EXIST;
@@ -69,13 +69,13 @@ bitmap_port_del(struct bitmap_port *map, uint16_t id)
static int
bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags)
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_port *map = set->data;
- uint32_t port = get_port(pf, skb, flags);
-
- if (port == IPSET_INVALID_PORT)
- return 0;
+ u16 port;
+
+ if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port))
+ return -EINVAL;
port = ntohs(port);
@@ -105,13 +105,13 @@ bitmap_port_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = {
static int
bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
struct bitmap_port *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
bool eexist = flags & IPSET_FLAG_EXIST;
- uint32_t port;
- uint16_t id, port_to;
+ u32 port; /* wraparound */
+ u16 id, port_to;
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
@@ -183,22 +183,16 @@ bitmap_port_head(struct ip_set *set, struct sk_buff *skb)
{
struct bitmap_port *map = set->data;
struct nlattr *nested;
- uint32_t id;
- uint16_t elements, last = map->last_port - map->first_port;
-
- for (id = 0, elements = 0; id <= last; id++)
- if (test_bit(id, map->members))
- elements++;
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port));
NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port));
- NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
- NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(map->memsize));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
+ htonl(sizeof(*map) + map->memsize));
ipset_nest_end(skb, nested);
return 0;
@@ -212,8 +206,8 @@ bitmap_port_list(struct ip_set *set,
{
struct bitmap_port *map = set->data;
struct nlattr *atd, *nested;
- uint16_t id, first = cb->args[2];
- uint16_t last = map->last_port - map->first_port;
+ u16 id, first = cb->args[2];
+ u16 last = map->last_port - map->first_port;
atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
@@ -246,6 +240,16 @@ nla_put_failure:
return 0;
}
+static bool
+bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
+{
+ struct bitmap_port *x = a->data;
+ struct bitmap_port *y = b->data;
+
+ return x->first_port == y->first_port
+ && x->last_port == y->last_port;
+}
+
const struct ip_set_type_variant bitmap_port __read_mostly = {
.kadt = bitmap_port_kadt,
.uadt = bitmap_port_uadt,
@@ -253,22 +257,23 @@ const struct ip_set_type_variant bitmap_port __read_mostly = {
.flush = bitmap_port_flush,
.head = bitmap_port_head,
.list = bitmap_port_list,
+ .same_set = bitmap_port_same_set,
};
/* Timeout variant */
struct bitmap_port_timeout {
void *members; /* the set members */
- uint16_t first_port; /* host byte order, included in range */
- uint16_t last_port; /* host byte order, included in range */
+ u16 first_port; /* host byte order, included in range */
+ u16 last_port; /* host byte order, included in range */
size_t memsize; /* members size */
- uint32_t timeout; /* timeout parameter */
+ u32 timeout; /* timeout parameter */
struct timer_list gc; /* garbage collection */
};
static inline bool
-bitmap_port_timeout_test(const struct bitmap_port_timeout *map, uint16_t id)
+bitmap_port_timeout_test(const struct bitmap_port_timeout *map, u16 id)
{
unsigned long *timeout = map->members;
@@ -277,7 +282,7 @@ bitmap_port_timeout_test(const struct bitmap_port_timeout *map, uint16_t id)
static int
bitmap_port_timeout_add(const struct bitmap_port_timeout *map,
- uint16_t id, uint32_t timeout)
+ u16 id, u32 timeout)
{
unsigned long *table = map->members;
@@ -291,7 +296,7 @@ bitmap_port_timeout_add(const struct bitmap_port_timeout *map,
static int
bitmap_port_timeout_del(const struct bitmap_port_timeout *map,
- uint16_t id)
+ u16 id)
{
unsigned long *table = map->members;
int ret = -IPSET_ERR_EXIST;
@@ -305,13 +310,13 @@ bitmap_port_timeout_del(const struct bitmap_port_timeout *map,
static int
bitmap_port_timeout_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags)
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_port_timeout *map = set->data;
- uint32_t port = get_port(pf, skb, flags);
-
- if (port == IPSET_INVALID_PORT)
- return 0;
+ u16 port;
+
+ if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port))
+ return -EINVAL;
port = ntohs(port);
@@ -334,13 +339,13 @@ bitmap_port_timeout_kadt(struct ip_set *set, const struct sk_buff *skb,
static int
bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
const struct bitmap_port_timeout *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
bool eexist = flags & IPSET_FLAG_EXIST;
- uint16_t port_to, id;
- uint32_t port, timeout = map->timeout;
+ u16 id, port_to;
+ u32 port, timeout = map->timeout; /* wraparound */
int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
@@ -372,7 +377,7 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
return -IPSET_ERR_BITMAP_RANGE;
if (tb[IPSET_ATTR_TIMEOUT])
- timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
for (; port <= port_to; port++) {
id = port - map->first_port;
@@ -394,10 +399,7 @@ bitmap_port_timeout_destroy(struct ip_set *set)
{
struct bitmap_port_timeout *map = set->data;
- /* gc might be running: del_timer_sync can't be used */
- while (!del_timer(&map->gc))
- msleep(IPSET_DESTROY_TIMER_SLEEP);
-
+ del_timer_sync(&map->gc);
ip_set_free(map->members, set->flags);
kfree(map);
@@ -417,23 +419,17 @@ bitmap_port_timeout_head(struct ip_set *set, struct sk_buff *skb)
{
struct bitmap_port_timeout *map = set->data;
struct nlattr *nested;
- uint32_t id;
- uint16_t elements, last = map->last_port - map->first_port;
-
- for (id = 0, elements = 0; id <= last; id++)
- if (bitmap_port_timeout_test(map, id))
- elements++;
-
+
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port));
NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT , htonl(map->timeout));
- NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(elements));
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
- NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(map->memsize));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
+ htonl(sizeof(*map) + map->memsize));
ipset_nest_end(skb, nested);
return 0;
@@ -447,8 +443,8 @@ bitmap_port_timeout_list(struct ip_set *set,
{
struct bitmap_port_timeout *map = set->data;
struct nlattr *adt, *nested;
- uint16_t id, first = cb->args[2];
- uint16_t last = map->last_port - map->first_port;
+ u16 id, first = cb->args[2];
+ u16 last = map->last_port - map->first_port;
unsigned long *table = map->members;
adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
@@ -485,6 +481,17 @@ nla_put_failure:
return 0;
}
+static bool
+bitmap_port_timeout_same_set(const struct ip_set *a, const struct ip_set *b)
+{
+ struct bitmap_port_timeout *x = a->data;
+ struct bitmap_port_timeout *y = b->data;
+
+ return x->first_port == y->first_port
+ && x->last_port == y->last_port
+ && x->timeout == y->timeout;
+}
+
const struct ip_set_type_variant bitmap_port_timeout __read_mostly = {
.kadt = bitmap_port_timeout_kadt,
.uadt = bitmap_port_timeout_uadt,
@@ -492,15 +499,17 @@ const struct ip_set_type_variant bitmap_port_timeout __read_mostly = {
.flush = bitmap_port_timeout_flush,
.head = bitmap_port_timeout_head,
.list = bitmap_port_timeout_list,
+ .same_set = bitmap_port_timeout_same_set,
};
static void
-bitmap_port_timeout_gc(unsigned long ul_set)
+bitmap_port_gc(unsigned long ul_set)
{
struct ip_set *set = (struct ip_set *) ul_set;
struct bitmap_port_timeout *map = set->data;
unsigned long *table = map->members;
- uint16_t id, last = map->last_port - map->first_port;
+ u32 id; /* wraparound */
+ u16 last = map->last_port - map->first_port;
/* We run parallel with other readers (test element)
* but adding/deleting new entries is locked out */
@@ -515,13 +524,13 @@ bitmap_port_timeout_gc(unsigned long ul_set)
}
static inline void
-bitmap_port_timeout_gc_init(struct ip_set *set)
+bitmap_port_gc_init(struct ip_set *set)
{
struct bitmap_port_timeout *map = set->data;
init_timer(&map->gc);
map->gc.data = (unsigned long) set;
- map->gc.function = bitmap_port_timeout_gc;
+ map->gc.function = bitmap_port_gc;
map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
add_timer(&map->gc);
}
@@ -537,7 +546,7 @@ bitmap_port_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
static bool
init_map_port(struct ip_set *set, struct bitmap_port *map,
- uint16_t first_port, uint16_t last_port)
+ u16 first_port, u16 last_port)
{
map->members = ip_set_alloc(map->memsize, GFP_KERNEL, &set->flags);
if (!map->members)
@@ -553,10 +562,10 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
static int
bitmap_port_create(struct ip_set *set, struct nlattr *head, int len,
- uint32_t flags)
+ u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
- uint16_t first_port, last_port;
+ u16 first_port, last_port;
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
bitmap_port_create_policy))
@@ -570,7 +579,7 @@ bitmap_port_create(struct ip_set *set, struct nlattr *head, int len,
if (tb[IPSET_ATTR_PORT_TO]) {
last_port = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
if (first_port > last_port) {
- uint16_t tmp = first_port;
+ u16 tmp = first_port;
first_port = last_port;
last_port = tmp;
@@ -594,11 +603,10 @@ bitmap_port_create(struct ip_set *set, struct nlattr *head, int len,
return -ENOMEM;
}
- map->timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
- set->flags |= IP_SET_FLAG_TIMEOUT;
+ map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
set->variant = &bitmap_port_timeout;
- bitmap_port_timeout_gc_init(set);
+ bitmap_port_gc_init(set);
} else {
struct bitmap_port *map;
@@ -607,7 +615,7 @@ bitmap_port_create(struct ip_set *set, struct nlattr *head, int len,
return -ENOMEM;
map->memsize = bitmap_bytes(0, last_port - first_port);
- D("memsize: %zu", map->memsize);
+ pr_debug("memsize: %zu", map->memsize);
if (!init_map_port(set, map, first_port, last_port)) {
kfree(map);
return -ENOMEM;
@@ -622,6 +630,7 @@ struct ip_set_type bitmap_port_type = {
.name = "bitmap:port",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_PORT,
+ .dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.create = bitmap_port_create,
diff --git a/kernel/ip_set_hash_ip.c b/kernel/ip_set_hash_ip.c
index d99c99b..e5ce6a4 100644
--- a/kernel/ip_set_hash_ip.c
+++ b/kernel/ip_set_hash_ip.c
@@ -7,6 +7,8 @@
/* Kernel module implementing an IP set type: the hash:ip type */
+#include <linux/netfilter/ip_set_kernel.h>
+#include <linux/netfilter/ip_set_jhash.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
@@ -19,6 +21,7 @@
#include <net/ipv6.h>
#include <net/netlink.h>
#include <net/pfxlen.h>
+#include <net/tcp.h>
#include <linux/netfilter.h>
#include <linux/netfilter/ip_set.h>
@@ -30,213 +33,125 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("hash:ip type of IP sets");
MODULE_ALIAS("ip_set_hash:ip");
-/* Member elements without timeout */
-struct ip4_elem {
- uint32_t ip;
-};
+/* Type specific function prefix */
+#define TYPE hash_ip
-struct ip6_elem {
- union nf_inet_addr ip;
-};
+static bool
+hash_ip_same_set(const struct ip_set *a, const struct ip_set *b);
-/* Member elements with timeout support */
-struct ip4_elem_timeout {
- uint32_t ip;
- unsigned long timeout;
-};
+#define hash_ip4_same_set hash_ip_same_set
+#define hash_ip6_same_set hash_ip_same_set
-struct ip6_elem_timeout {
- union nf_inet_addr ip;
- unsigned long timeout;
-};
+/* The type variant functions: IPv4 */
-/* The hash:ip type structure */
-struct hash_ip {
- void *members; /* the set members */
- uint32_t hashsize; /* hash size */
- uint32_t maxelem; /* max number of elements/hashsize */
- uint8_t probes; /* max number of probes */
- uint8_t resize; /* resize factor in percent */
- uint8_t netmask; /* netmask */
- uint32_t timeout; /* timeout value */
- uint32_t elements; /* number of elements */
- struct timer_list gc; /* garbage collector */
- size_t elem_size; /* size of element */
- initval_t initval[0]; /* initvals for jhash_1word */
+/* Member elements without timeout */
+struct hash_ip4_elem {
+ u32 ip;
};
-static inline void *
-hash_ip_elem(const struct hash_ip *map, uint32_t id)
-{
- return (void *)((char *)map->members + id * map->elem_size);
-}
-
-static inline unsigned long
-get_ip4_elem_timeout(const struct ip4_elem *elem)
-{
- return ((const struct ip4_elem_timeout *)elem)->timeout;
-}
-
-static inline unsigned long
-get_ip6_elem_timeout(const struct ip6_elem *elem)
-{
- return ((const struct ip6_elem_timeout *)elem)->timeout;
-}
-
-static inline uint32_t
-ip4_hash(struct ip4_elem *elem, initval_t initval, uint32_t hashsize)
-{
- return jhash_1word(elem->ip, initval) % hashsize;
-}
-
-static inline uint32_t
-ip6_hash(struct ip6_elem *elem, initval_t initval, uint32_t hashsize)
-{
- return jhash2((u32 *)&elem->ip, 4, initval) % hashsize;
-}
+/* Member elements with timeout support */
+struct hash_ip4_telem {
+ u32 ip;
+ unsigned long timeout;
+};
static inline bool
-ip4_cmp(struct ip4_elem *ip1, struct ip4_elem *ip2)
+hash_ip4_data_equal(const struct hash_ip4_elem *ip1,
+ const struct hash_ip4_elem *ip2)
{
return ip1->ip == ip2->ip;
}
static inline bool
-ip6_cmp(struct ip6_elem *ip1, struct ip6_elem *ip2)
-{
- return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6);
-}
-
-static inline bool
-ip4_null(struct ip4_elem *elem)
+hash_ip4_data_isnull(const struct hash_ip4_elem *elem)
{
return elem->ip == 0;
}
-static inline bool
-ip6_null(struct ip6_elem *elem)
-{
- return ipv6_addr_any(&elem->ip.in6);
-}
-
static inline void
-ip4_cpy(struct ip4_elem *dst, const struct ip4_elem *src)
+hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src)
{
dst->ip = src->ip;
}
static inline void
-ip6_cpy(struct ip6_elem *dst, const struct ip6_elem *src)
+hash_ip4_data_swap(struct hash_ip4_elem *dst, struct hash_ip4_elem *src)
{
- ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
+ swap(dst->ip, src->ip);
}
-/* Zero valued IP addresses (network order) cannot be stored */
+/* Zero valued IP addresses cannot be stored */
static inline void
-ip4_zero_out(struct ip4_elem *elem)
+hash_ip4_data_zero_out(struct hash_ip4_elem *elem)
{
elem->ip = 0;
}
-static inline void
-ip6_zero_out(struct ip6_elem *elem)
+static inline bool
+hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data)
{
- ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0);
-}
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
+ return 0;
-static inline void
-ip6_netmask(union nf_inet_addr *ip, uint8_t prefix)
-{
- ip->ip6[0] &= NETMASK6(prefix)[0];
- ip->ip6[1] &= NETMASK6(prefix)[1];
- ip->ip6[2] &= NETMASK6(prefix)[2];
- ip->ip6[3] &= NETMASK6(prefix)[3];
+nla_put_failure:
+ return 1;
}
-/* The type variant functions: generic ones */
-
-static void
-hash_ip_destroy(struct ip_set *set)
+static inline bool
+hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data)
{
- struct hash_ip *map = set->data;
+ const struct hash_ip4_telem *tdata =
+ (const struct hash_ip4_telem *)data;
- /* gc might be running: del_timer_sync can't be used */
- if (set->flags & IP_SET_FLAG_TIMEOUT)
- while (!del_timer(&map->gc))
- msleep(IPSET_DESTROY_TIMER_SLEEP);
-
- ip_set_free(map->members, set->flags);
- kfree(map);
-
- set->data = NULL;
-}
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(tdata->timeout)));
-#define hash_ip4_destroy hash_ip_destroy
-#define hash_ip6_destroy hash_ip_destroy
+ return 0;
-static void
-hash_ip_flush(struct ip_set *set)
-{
- struct hash_ip *map = set->data;
-
- memset(map->members, 0, map->hashsize * map->elem_size);
- map->elements = 0;
+nla_put_failure:
+ return 1;
}
-#define hash_ip4_flush hash_ip_flush
-#define hash_ip6_flush hash_ip_flush
-
-/* IPv4 variant */
-
-#define PF 4
-#include "ip_set_hash_ip_src.c"
-#undef PF
+#define IP_SET_HASH_WITH_NETMASK
+#define PF 4
+#define HOST_MASK 32
+#include <linux/netfilter/ip_set_chash.h>
static int
hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags)
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
- struct hash_ip *map = set->data;
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
- uint32_t ip;
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ u32 ip;
- if (pf != AF_INET)
- return -EINVAL;
-
- ip4addrptr(skb, flags, &ip);
- ip &= NETMASK(map->netmask);
+ ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip);
+ ip &= NETMASK(h->netmask);
if (ip == 0)
return -EINVAL;
- switch (adt) {
- case IPSET_TEST:
- return hash_ip4_test(map, with_timeout,
- (struct ip4_elem *)&ip);
- case IPSET_ADD:
- return hash_ip4_add(map, with_timeout,
- (struct ip4_elem *)&ip, map->timeout);
- case IPSET_DEL:
- return hash_ip4_del(map, with_timeout, (struct ip4_elem *)&ip);
- default:
- BUG();
- }
- return 0;
+ return adtfn(set, &ip, GFP_ATOMIC, h->timeout);
}
static const struct nla_policy
hash_ip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP] = { .type = NLA_U32 },
+ [IPSET_ATTR_IP_TO] = { .type = NLA_U32 },
+ [IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
static int
hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
- struct hash_ip *map = set->data;
+ struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
- uint32_t ip, timeout = map->timeout;
+ bool eexist = flags & IPSET_FLAG_EXIST;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ u32 ip, nip, ip_to, hosts, timeout = h->timeout;
+ int ret = 0;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip4_adt_policy))
@@ -247,69 +162,164 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
else
return -IPSET_ERR_PROTOCOL;
- ip &= NETMASK(map->netmask);
+ ip &= NETMASK(h->netmask);
if (ip == 0)
return -IPSET_ERR_HASH_ELEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
- if (!with_timeout)
+ if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
- timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- switch (adt) {
- case IPSET_TEST:
- return hash_ip4_test(map, with_timeout,
- (struct ip4_elem *)&ip);
- case IPSET_ADD:
- return hash_ip4_add(map, with_timeout,
- (struct ip4_elem *)&ip, timeout);
- case IPSET_DEL:
- return hash_ip4_del(map, with_timeout,
- (struct ip4_elem *)&ip);
- default:
- BUG();
+ if (adt == IPSET_TEST)
+ return adtfn(set, &ip, GFP_KERNEL, timeout);
+
+ ip = ntohl(ip);
+ if (tb[IPSET_ATTR_IP_TO]) {
+ ip_to = ip_set_get_h32(tb[IPSET_ATTR_IP_TO]);
+ if (ip > ip_to)
+ swap(ip, ip_to);
+ } else if (tb[IPSET_ATTR_CIDR]) {
+ u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+
+ if (cidr > 32)
+ return -IPSET_ERR_INVALID_CIDR;
+ ip &= HOSTMASK(cidr);
+ ip_to = ip | ~HOSTMASK(cidr);
+ } else
+ ip_to = ip;
+
+ hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
+
+ for (; !before(ip_to, ip); ip += hosts) {
+ nip = htonl(ip);
+ ret = adtfn(set, &nip, GFP_KERNEL, timeout);
+
+ if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+ return ret;
+ }
}
+ return ret;
+}
+
+static bool
+hash_ip_same_set(const struct ip_set *a, const struct ip_set *b)
+{
+ struct chash *x = a->data;
+ struct chash *y = b->data;
+
+ return x->maxelem == y->maxelem
+ && x->timeout == y->timeout
+ && x->htable_bits == y->htable_bits /* resizing ? */
+ && x->array_size == y->array_size
+ && x->chain_limit == y->chain_limit
+ && x->netmask == y->netmask;
+}
+
+/* The type variant functions: IPv6 */
+
+struct hash_ip6_elem {
+ union nf_inet_addr ip;
+};
+struct hash_ip6_telem {
+ union nf_inet_addr ip;
+ unsigned long timeout;
+};
+
+static inline bool
+hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
+ const struct hash_ip6_elem *ip2)
+{
+ return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0;
+}
+
+static inline bool
+hash_ip6_data_isnull(const struct hash_ip6_elem *elem)
+{
+ return ipv6_addr_any(&elem->ip.in6);
+}
+
+static inline void
+hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src)
+{
+ ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
+}
+
+static inline void
+hash_ip6_data_swap(struct hash_ip6_elem *dst, struct hash_ip6_elem *src)
+{
+ struct in6_addr tmp;
+
+ ipv6_addr_copy(&tmp, &dst->ip.in6);
+ ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
+ ipv6_addr_copy(&src->ip.in6, &tmp);
+}
+
+static inline void
+hash_ip6_data_zero_out(struct hash_ip6_elem *elem)
+{
+ ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0);
+}
+
+static inline void
+ip6_netmask(union nf_inet_addr *ip, u8 prefix)
+{
+ ip->ip6[0] &= NETMASK6(prefix)[0];
+ ip->ip6[1] &= NETMASK6(prefix)[1];
+ ip->ip6[2] &= NETMASK6(prefix)[2];
+ ip->ip6[3] &= NETMASK6(prefix)[3];
+}
+
+static inline bool
+hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data)
+{
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
return 0;
+
+nla_put_failure:
+ return 1;
}
-/* IPv6 variants */
+static inline bool
+hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data)
+{
+ const struct hash_ip6_telem *e =
+ (const struct hash_ip6_telem *)data;
+
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(e->timeout)));
+ return 0;
+
+nla_put_failure:
+ return 1;
+}
-#define PF 6
-#include "ip_set_hash_ip_src.c"
#undef PF
+#undef HOST_MASK
+
+#define PF 6
+#define HOST_MASK 128
+#include <linux/netfilter/ip_set_chash.h>
static int
hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags)
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
- struct hash_ip *map = set->data;
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr ip;
- if (pf != AF_INET6)
- return -EINVAL;
-
- ip6addrptr(skb, flags, &ip.in6);
- ip6_netmask(&ip, map->netmask);
+ ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6);
+ ip6_netmask(&ip, h->netmask);
if (ipv6_addr_any(&ip.in6))
return -EINVAL;
- switch (adt) {
- case IPSET_TEST:
- return hash_ip6_test(map, with_timeout,
- (struct ip6_elem *)&ip);
- case IPSET_ADD:
- return hash_ip6_add(map, with_timeout,
- (struct ip6_elem *)&ip, map->timeout);
- case IPSET_DEL:
- return hash_ip6_del(map, with_timeout,
- (struct ip6_elem *)&ip);
- default:
- BUG();
- }
- return 0;
+ return adtfn(set, &ip, GFP_ATOMIC, h->timeout);
}
static const struct nla_policy
@@ -321,13 +331,13 @@ hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
static int
hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags)
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
- struct hash_ip *map = set->data;
+ struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr *ip;
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
- uint32_t timeout = map->timeout;
+ u32 timeout = h->timeout;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip6_adt_policy))
@@ -338,31 +348,17 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len,
else
return -IPSET_ERR_PROTOCOL;
- ip6_netmask(ip, map->netmask);
+ ip6_netmask(ip, h->netmask);
if (ipv6_addr_any(&ip->in6))
return -IPSET_ERR_HASH_ELEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
- if (!with_timeout)
+ if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
- timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- switch (adt) {
- case IPSET_TEST:
- return hash_ip6_test(map, with_timeout,
- (struct ip6_elem *)ip);
- case IPSET_ADD:
- return hash_ip6_add(map, with_timeout,
- (struct ip6_elem *)ip, timeout);
- case IPSET_DEL:
- return hash_ip6_del(map, with_timeout,
- (struct ip6_elem *)ip);
- default:
- BUG();
- }
-
- return 0;
+ return adtfn(set, ip, GFP_KERNEL, timeout);
}
/* Create hash:ip type of sets */
@@ -374,114 +370,84 @@ hash_ip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
};
-static bool
-init_map_ip(struct ip_set *set, struct hash_ip *map, uint32_t maxelem,
- uint32_t probes, uint32_t resize, uint8_t netmask, uint8_t family)
-{
- map->members = ip_set_alloc(map->hashsize * map->elem_size,
- GFP_KERNEL, &set->flags);
- if (!map->members)
- return false;
-
- map->maxelem = maxelem;
- map->probes = probes;
- map->resize = resize;
- map->netmask = netmask;
-
- set->data = map;
- set->family = family;
-
- return true;
-}
-
static int
-hash_ip_create(struct ip_set *set, struct nlattr *head, int len,
- uint32_t flags)
+hash_ip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
- uint32_t hashsize, maxelem;
- uint8_t probes, resize, netmask, family, i;
- struct hash_ip *map;
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+ u8 netmask;
+ struct chash *h;
+
+ if (!(set->family == AF_INET || set->family == AF_INET6))
+ return -IPSET_ERR_INVALID_FAMILY;
+ netmask = set->family == AF_INET ? 32 : 128;
+ pr_debug("Create set %s with family %s",
+ set->name, set->family == AF_INET ? "inet" : "inet6");
if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
hash_ip_create_policy))
return -IPSET_ERR_PROTOCOL;
- hashsize = IPSET_DEFAULT_HASHSIZE;
- maxelem = IPSET_DEFAULT_MAXELEM;
- probes = IPSET_DEFAULT_PROBES;
- resize = IPSET_DEFAULT_RESIZE;
- family = AF_INET;
-
- if (tb[IPSET_ATTR_HASHSIZE])
+ if (tb[IPSET_ATTR_HASHSIZE]) {
hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
+ if (hashsize < IPSET_MIMINAL_HASHSIZE)
+ hashsize = IPSET_MIMINAL_HASHSIZE;
+ }
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
- if (tb[IPSET_ATTR_PROBES])
- probes = nla_get_u8(tb[IPSET_ATTR_PROBES]);
-
- if (tb[IPSET_ATTR_RESIZE])
- resize = nla_get_u8(tb[IPSET_ATTR_RESIZE]);
-
- if (tb[IPSET_ATTR_FAMILY])
- family = nla_get_u8(tb[IPSET_ATTR_FAMILY]);
- if (!(family == AF_INET || family == AF_INET6))
- return -IPSET_ERR_INVALID_FAMILY;
- netmask = family == AF_INET ? 32 : 128;
-
if (tb[IPSET_ATTR_NETMASK]) {
netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
- if ((family == AF_INET && netmask > 32)
- || (family == AF_INET6 && netmask > 128))
+ if ((set->family == AF_INET && netmask > 32)
+ || (set->family == AF_INET6 && netmask > 128)
+ || netmask == 0)
return -IPSET_ERR_INVALID_NETMASK;
}
- map = kzalloc(sizeof(*map) + probes * sizeof(initval_t), GFP_KERNEL);
- if (!map)
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
return -ENOMEM;
-
- map->hashsize = hashsize;
- if (tb[IPSET_ATTR_TIMEOUT]) {
- map->elem_size = family == AF_INET
- ? sizeof(struct ip4_elem_timeout)
- : sizeof(struct ip6_elem_timeout);
-
- if (!init_map_ip(set, map, maxelem, probes, resize, netmask,
- family)) {
- kfree(map);
- return -ENOMEM;
- }
- map->timeout = ip_set_get_h32(tb[IPSET_ATTR_TIMEOUT]);
- set->flags |= IP_SET_FLAG_TIMEOUT;
+ h->maxelem = maxelem;
+ h->netmask = netmask;
+ h->htable_bits = htable_bits(hashsize);
+ h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
+ h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
+ get_random_bytes(&h->initval, sizeof(h->initval));
+ h->timeout = IPSET_NO_TIMEOUT;
+
+ h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL, &set->flags);
+ if (!h->htable) {
+ kfree(h);
+ return -ENOMEM;
+ }
+
+ set->data = h;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- if (family == AF_INET)
+ set->variant = set->family == AF_INET
+ ? &hash_ip4_tvariant : &hash_ip6_tvariant;
+
+ if (set->family == AF_INET)
hash_ip4_gc_init(set);
else
hash_ip6_gc_init(set);
} else {
- map->elem_size = family == AF_INET
- ? sizeof(struct ip4_elem)
- : sizeof(struct ip6_elem);
-
- if (!init_map_ip(set, map, maxelem, probes, resize, netmask,
- family)) {
- kfree(map);
- return -ENOMEM;
- }
+ set->variant = set->family == AF_INET
+ ? &hash_ip4_variant : &hash_ip6_variant;
}
- for (i = 0; i < map->probes; i++)
- get_random_bytes(((initval_t *) map->initval)+i,
- sizeof(initval_t));
- set->variant = family == AF_INET ? &hash_ip4 : &hash_ip6;
- D("create %s hashsize %u maxelem %u probes %u resize %u",
- set->name, map->hashsize, map->maxelem, map->probes, map->resize);
+ pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
+ set->name, jhash_size(h->htable_bits),
+ h->htable_bits, h->maxelem, set->data, h->htable);
return 0;
}
@@ -490,6 +456,7 @@ static struct ip_set_type hash_ip_type = {
.name = "hash:ip",
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP,
+ .dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
.create = hash_ip_create,
diff --git a/kernel/ip_set_hash_ip_src.c b/kernel/ip_set_hash_ip_src.c
deleted file mode 100644
index ef0a8ec..0000000
--- a/kernel/ip_set_hash_ip_src.c
+++ /dev/null
@@ -1,473 +0,0 @@
-/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#define CONCAT(a, b, c) a##b##c
-#define TOKEN(a, b, c) CONCAT(a, b, c)
-
-/* IPv4/IPv6 dependent function prototypes for hash:ip */
-
-#if PF == 4
-#define HOST_MASK 32
-#define NLA_PUT_ADDR(skb, ip) \
- NLA_PUT_NET32(skb, IPSET_ATTR_IP, *(ip));
-#else
-#define HOST_MASK 128
-#define NLA_PUT_ADDR(skb, ip) \
- NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), ip);
-#endif
-
-#define hash_ip_pf_timeout TOKEN(hash_ip, PF, _timeout)
-#define hash_ip_pf_expired TOKEN(hash_ip, PF, _expired)
-#define hash_ip_pf_elem_test TOKEN(hash_ip, PF, _elem_test)
-#define hash_ip_pf_elem_exist TOKEN(hash_ip, PF, _elem_exist)
-#define hash_ip_pf_elem_expired TOKEN(hash_ip, PF, _elem_expired)
-#define hash_ip_pf_test TOKEN(hash_ip, PF, _test)
-#define hash_ip_pf_add TOKEN(hash_ip, PF, _add)
-#define hash_ip_pf_readd TOKEN(hash_ip, PF, _readd)
-#define hash_ip_pf_del TOKEN(hash_ip, PF, _del)
-#define hash_ip_pf_map_expired TOKEN(hash_ip, PF, _map_expired)
-#define hash_ip_pf_set_expired TOKEN(hash_ip, PF, _set_expired)
-#define hash_ip_pf_head TOKEN(hash_ip, PF, _head)
-#define hash_ip_pf_list TOKEN(hash_ip, PF, _list)
-#define hash_ip_pf_resize TOKEN(hash_ip, PF, _resize)
-#define hash_ip_pf TOKEN(hash_ip, PF , )
-#define hash_ip_pf_kadt TOKEN(hash_ip, PF, _kadt)
-#define hash_ip_pf_uadt TOKEN(hash_ip, PF, _uadt)
-#define hash_ip_pf_destroy TOKEN(hash_ip, PF, _destroy)
-#define hash_ip_pf_flush TOKEN(hash_ip, PF, _flush)
-#define hash_ip_pf_timeout_gc TOKEN(hash_ip, PF, _timeout_gc)
-#define hash_ip_pf_gc_init TOKEN(hash_ip, PF, _gc_init)
-#define ip_pf_hash TOKEN(ip, PF, _hash)
-#define ip_pf_cmp TOKEN(ip, PF, _cmp)
-#define ip_pf_null TOKEN(ip, PF, _null)
-#define ip_pf_cpy TOKEN(ip, PF, _cpy)
-#define ip_pf_zero_out TOKEN(ip, PF, _zero_out)
-#define ip_pf_elem TOKEN(ip, PF, _elem)
-#define ip_pf_elem_timeout TOKEN(ip, PF, _elem_timeout)
-#define ip_pf_get_elem_timeout TOKEN(get_ip, PF, _elem_timeout)
-
-static inline bool
-hash_ip_pf_timeout(const struct hash_ip *map, uint32_t id)
-{
- struct ip_pf_elem_timeout *elem = hash_ip_elem(map, id);
-
- return ip_set_timeout_test(elem->timeout);
-}
-
-static inline bool
-hash_ip_pf_expired(const struct hash_ip *map, uint32_t id)
-{
- struct ip_pf_elem_timeout *elem = hash_ip_elem(map, id);
-
- return ip_set_timeout_expired(elem->timeout);
-}
-
-static inline bool
-hash_ip_pf_elem_test(const struct hash_ip *map, bool with_timeout,
- uint32_t id, struct ip_pf_elem * ip)
-{
- struct ip_pf_elem *elem = hash_ip_elem(map, id);
-
- return ip_pf_cmp(elem, ip)
- && (!with_timeout || hash_ip_pf_timeout(map, id));
-}
-
-static inline bool
-hash_ip_pf_elem_exist(const struct hash_ip *map, bool with_timeout,
- uint32_t id)
-{
- struct ip_pf_elem *elem = hash_ip_elem(map, id);
-
- return !(ip_pf_null(elem)
- || (with_timeout && hash_ip_pf_expired(map, id)));
-}
-
-static inline bool
-hash_ip_pf_elem_expired(const struct hash_ip *map, bool with_timeout,
- uint32_t id)
-{
- struct ip_pf_elem *elem = hash_ip_elem(map, id);
-
- return ip_pf_null(elem)
- || (with_timeout && hash_ip_pf_expired(map, id));
-}
-
-static inline uint32_t
-hash_ip_pf_test(const struct hash_ip *map, bool with_timeout,
- struct ip_pf_elem * ip)
-{
- uint32_t id;
- uint8_t i;
-
- for (i = 0; i < map->probes; i++) {
- id = ip_pf_hash(ip, *(map->initval + i), map->hashsize);
- if (hash_ip_pf_elem_test(map, with_timeout, id, ip))
- return id + 1;
- /* No shortcut - there can be deleted entries. */
- }
- return 0;
-}
-
-static void
-hash_ip_pf_map_expired(struct hash_ip *map)
-{
- struct ip_pf_elem_timeout *table = map->members;
- uint32_t i;
-
- /* We run parallel with other readers (test element)
- * but adding/deleting new entries is locked out */
- for (i = 0; i < map->hashsize; i++)
- if (ip_set_timeout_expired(table[i].timeout)) {
- ip_pf_zero_out((struct ip_pf_elem *)&table[i]);
- table[i].timeout = IPSET_ELEM_UNSET;
- map->elements--;
- }
-}
-
-static inline void
-hash_ip_pf_set_expired(struct ip_set *set)
-{
- /* We run parallel with other readers (test element)
- * but adding/deleting new entries is locked out */
- read_lock_bh(&set->lock);
- hash_ip_pf_map_expired(set->data);
- read_unlock_bh(&set->lock);
-}
-
-static int
-hash_ip_pf_add(struct hash_ip *map, bool with_timeout,
- struct ip_pf_elem *ip, uint32_t timeout)
-{
- uint32_t id, empty = 0;
- uint8_t i;
-
- if (map->elements >= map->maxelem) {
- if (with_timeout) {
- hash_ip_pf_map_expired(map);
- if (map->elements < map->maxelem)
- goto doit;
- }
- return -IPSET_ERR_HASH_FULL;
- }
-
-doit:
- for (i = 0; i < map->probes; i++) {
- id = ip_pf_hash(ip, *(map->initval + i), map->hashsize);
- if (hash_ip_pf_elem_test(map, with_timeout, id, ip))
- return -IPSET_ERR_EXIST;
- if (empty == 0
- && hash_ip_pf_elem_expired(map, with_timeout, id))
- empty = id + 1;
- /* There can be deleted entries, must check all slots */
- }
- if (!empty)
- /* Trigger rehashing */
- return -EAGAIN;
-
- if (with_timeout) {
- struct ip_pf_elem_timeout *e = hash_ip_elem(map, empty - 1);
- e->timeout = ip_set_timeout_set(timeout);
- D("add with timeout: %u (%lu)", timeout, e->timeout);
- ip_pf_cpy((struct ip_pf_elem *)e, ip);
- } else {
- struct ip_pf_elem *e = hash_ip_elem(map, empty - 1);
- ip_pf_cpy(e, ip);
- }
- map->elements++;
- return 0;
-}
-
-static int
-hash_ip_pf_readd(struct hash_ip *map, bool with_timeout, struct ip_pf_elem *ip)
-{
- uint32_t id, empty = 0;
- uint8_t i;
-
- for (i = 0; empty == 0 && i < map->probes; i++) {
- id = ip_pf_hash(ip, *(map->initval + i), map->hashsize);
- if (ip_pf_null(hash_ip_elem(map, id)))
- empty = id + 1;
- }
- if (!empty)
- /* Trigger rehashing */
- return -EAGAIN;
-
- if (with_timeout) {
- struct ip_pf_elem_timeout *e = hash_ip_elem(map, empty - 1);
- e->timeout = ip_pf_get_elem_timeout(ip);
- ip_pf_cpy((struct ip_pf_elem *)e, ip);
- } else {
- struct ip_pf_elem *e = hash_ip_elem(map, empty - 1);
- ip_pf_cpy(e, ip);
- }
- map->elements++;
- return 0;
-}
-
-static int
-hash_ip_pf_del(struct hash_ip *map, bool with_timeout, struct ip_pf_elem *ip)
-{
- struct ip_pf_elem *e;
- uint32_t id, found = 0;
- uint8_t i;
-
- for (i = 0; i < map->probes; i++) {
- id = ip_pf_hash(ip, *(map->initval + i), map->hashsize);
- if (hash_ip_pf_elem_test(map, with_timeout, id, ip)) {
- found = id + 1;
- break;
- }
- }
- if (!found)
- return -IPSET_ERR_EXIST;
-
- e = hash_ip_elem(map, found - 1);
- ip_pf_zero_out(e);
- if (with_timeout)
- ((struct ip_pf_elem_timeout *)e)->timeout = IPSET_ELEM_UNSET;
-
- map->elements--;
-
- return 0;
-}
-
-static int
-hash_ip_pf_head(struct ip_set *set, struct sk_buff *skb)
-{
- const struct hash_ip *map = set->data;
- struct nlattr *nested;
-
- if (set->flags & IP_SET_FLAG_TIMEOUT)
- hash_ip_pf_set_expired(set);
-
- nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
- if (!nested)
- goto nla_put_failure;
- NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE, htonl(map->hashsize));
- NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(map->maxelem));
- if (map->netmask != HOST_MASK)
- NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask);
- NLA_PUT_U8(skb, IPSET_ATTR_PROBES, map->probes);
- NLA_PUT_U8(skb, IPSET_ATTR_RESIZE, map->resize);
- NLA_PUT_NET32(skb, IPSET_ATTR_ELEMENTS, htonl(map->elements));
- NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
- htonl(atomic_read(&set->ref) - 1));
- NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
- htonl(map->hashsize * map->elem_size));
- if (set->flags & IP_SET_FLAG_TIMEOUT)
- NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
- ipset_nest_end(skb, nested);
-
- return 0;
-nla_put_failure:
- return -EFAULT;
-}
-
-static int
-hash_ip_pf_list(struct ip_set *set,
- struct sk_buff *skb, struct netlink_callback *cb)
-{
- const struct hash_ip *map = set->data;
- struct nlattr *atd, *nested;
- struct ip_pf_elem *elem;
- uint32_t id, first = cb->args[2];
- bool with_timeout = set->flags & IP_SET_FLAG_TIMEOUT;
-
- atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
- if (!atd)
- return -EFAULT;
- for (; cb->args[2] < map->hashsize; cb->args[2]++) {
- id = cb->args[2];
- if (hash_ip_pf_elem_expired(map, with_timeout, id))
- continue;
- nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
- if (!nested) {
- if (id == first) {
- nla_nest_cancel(skb, atd);
- return -EFAULT;
- } else
- goto nla_put_failure;
- }
- elem = hash_ip_elem(map, id);
- NLA_PUT_ADDR(skb, &elem->ip);
- if (map->netmask != HOST_MASK)
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR, map->netmask);
- if (with_timeout) {
- unsigned long timeout = ip_pf_get_elem_timeout(elem);
- D("list with timeout: %u (%lu)",
- ip_set_timeout_get(timeout), timeout);
- NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
- htonl(ip_set_timeout_get(timeout)));
- }
- ipset_nest_end(skb, nested);
- }
- ipset_nest_end(skb, atd);
- /* Set listing finished */
- cb->args[2] = 0;
-
- return 0;
-
-nla_put_failure:
- nla_nest_cancel(skb, nested);
- ipset_nest_end(skb, atd);
- return 0;
-}
-
-static int
-hash_ip_pf_resize(struct ip_set *set, uint8_t retried)
-{
- struct hash_ip *map = set->data, *tmp;
- void *members;
- uint32_t i, hashsize = map->hashsize;
- uint8_t oflags, flags = set->flags;
- bool with_timeout = flags & IP_SET_FLAG_TIMEOUT;
- int ret;
-
- if (map->resize == 0)
- return -IPSET_ERR_HASH_FULL;
-
- /* Try to cleanup first */
- if (retried == 0 && with_timeout) {
- i = map->elements;
- hash_ip_pf_set_expired(set);
- if (map->elements < i)
- return 0;
- }
-
-again:
- ret = 0;
-
- /* Calculate new hash size */
- hashsize += (hashsize * map->resize)/100;
- if (hashsize == map->hashsize)
- hashsize++;
- if (hashsize >= map->maxelem)
- return -IPSET_ERR_HASH_FULL;
-
- printk("Rehashing of set %s triggered: hash grows from %lu to %lu\n",
- set->name,
- (long unsigned)map->hashsize,
- (long unsigned)hashsize);
-
- tmp = kmalloc(sizeof(struct hash_ip)
- + map->probes * sizeof(initval_t), GFP_ATOMIC);
- if (!tmp)
- return -ENOMEM;
-
- memcpy(tmp, map, sizeof(*map) + map->probes * sizeof(initval_t));
- tmp->elements = 0;
- tmp->hashsize = hashsize;
- tmp->members = ip_set_alloc(hashsize * map->elem_size,
- GFP_ATOMIC, &flags);
- if (!tmp->members) {
- kfree(tmp);
- return -ENOMEM;
- }
-
- write_lock_bh(&set->lock);
- map = set->data; /* Play safe */
- for (i = 0; i < map->hashsize && ret == 0; i++) {
- if (hash_ip_pf_elem_exist(map, with_timeout, i))
- ret = hash_ip_pf_readd(tmp, with_timeout,
- hash_ip_elem(map, i));
- }
- if (ret) {
- /* Failure, try again */
- write_unlock_bh(&set->lock);
- ip_set_free(tmp->members, flags);
- kfree(tmp);
- goto again;
- }
-
- /* Success at resizing! */
- members = map->members;
- oflags = set->flags;
-
- map->hashsize = tmp->hashsize;
- map->members = tmp->members;
- map->elements = tmp->elements;
- set->flags = flags;
- write_unlock_bh(&set->lock);
-
- ip_set_free(members, oflags);
- kfree(tmp);
-
- return 0;
-}
-
-static int
-hash_ip_pf_kadt(struct ip_set *set, const struct sk_buff * skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags);
-static int
-hash_ip_pf_uadt(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags);
-
-static const struct ip_set_type_variant hash_ip_pf __read_mostly = {
- .kadt = hash_ip_pf_kadt,
- .uadt = hash_ip_pf_uadt,
- .destroy = hash_ip_pf_destroy,
- .flush = hash_ip_pf_flush,
- .head = hash_ip_pf_head,
- .list = hash_ip_pf_list,
- .resize = hash_ip_pf_resize,
-};
-
-static void
-hash_ip_pf_timeout_gc(unsigned long ul_set)
-{
- struct ip_set *set = (struct ip_set *) ul_set;
- struct hash_ip *map = set->data;
-
- hash_ip_pf_set_expired(set);
-
- map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
- add_timer(&map->gc);
-}
-
-static inline void
-hash_ip_pf_gc_init(struct ip_set *set)
-{
- struct hash_ip *map = set->data;
-
- init_timer(&map->gc);
- map->gc.data = (unsigned long) set;
- map->gc.function = hash_ip_pf_timeout_gc;
- map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
- add_timer(&map->gc);
-}
-
-#undef HOST_MASK
-#undef NLA_PUT_ADDR
-#undef hash_ip_pf_timeout
-#undef hash_ip_pf_expired
-#undef hash_ip_pf_elem_test
-#undef hash_ip_pf_elem_exist
-#undef hash_ip_pf_elem_expired
-#undef hash_ip_pf_test
-#undef hash_ip_pf_add
-#undef hash_ip_pf_readd
-#undef hash_ip_pf_del
-#undef hash_ip_pf_map_expired
-#undef hash_ip_pf_set_expired
-#undef hash_ip_pf_head
-#undef hash_ip_pf_list
-#undef hash_ip_pf_resize
-#undef hash_ip_pf
-#undef hash_ip_pf_kadt
-#undef hash_ip_pf_uadt
-#undef hash_ip_pf_destroy
-#undef hash_ip_pf_flush
-#undef hash_ip_pf_timeout_gc
-#undef hash_ip_pf_gc_init
-#undef ip_pf_hash
-#undef ip_pf_cmp
-#undef ip_pf_null
-#undef ip_pf_cpy
-#undef ip_pf_zero_out
-#undef ip_pf_elem
-#undef ip_pf_elem_timeout
-#undef ip_pf_get_elem_timeout
diff --git a/kernel/ip_set_hash_ipport.c b/kernel/ip_set_hash_ipport.c
index 36e68b0..8210f67 100644
--- a/kernel/ip_set_hash_ipport.c
+++ b/kernel/ip_set_hash_ipport.c
@@ -1,197 +1,455 @@
-/* Copyright (C) 2003-2008 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-/* Kernel module implementing an ip+port hash set */
+/* Kernel module implementing an IP set type: the hash:ip,port type */
+#include <linux/netfilter/ip_set_kernel.h>
+#include <linux/netfilter/ip_set_jhash.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
#include <linux/skbuff.h>
-#include <linux/netfilter_ipv4/ip_set_jhash.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
-
#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+#include <net/pfxlen.h>
-#include <linux/netfilter_ipv4/ip_set_ipporthash.h>
-#include <linux/netfilter_ipv4/ip_set_getport.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/ip_set.h>
+#include <linux/netfilter/ip_set_timeout.h>
+#include <linux/netfilter/ip_set_getport.h>
+#include <linux/netfilter/ip_set_hash.h>
-static int limit = MAX_RANGE;
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
+MODULE_DESCRIPTION("hash:ip,port type of IP sets");
+MODULE_ALIAS("ip_set_hash:ip,port");
+
+/* Type specific function prefix */
+#define TYPE hash_ipport
+
+static bool
+hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b);
+
+#define hash_ipport4_same_set hash_ipport_same_set
+#define hash_ipport6_same_set hash_ipport_same_set
+
+/* The type variant functions: IPv4 */
+
+/* Member elements without timeout */
+struct hash_ipport4_elem {
+ u32 ip;
+ u16 port;
+ u16 match;
+};
+
+/* Member elements with timeout support */
+struct hash_ipport4_telem {
+ u32 ip;
+ u16 port;
+ u16 match;
+ unsigned long timeout;
+};
+
+static inline bool
+hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
+ const struct hash_ipport4_elem *ip2)
+{
+ return ip1->ip == ip2->ip && ip1->port == ip2->port;
+}
-static inline __u32
-ipporthash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port)
+static inline bool
+hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem)
{
- struct ip_set_ipporthash *map = set->data;
- __u32 id;
- u_int16_t i;
- ip_set_ip_t *elem;
+ return elem->match == 0;
+}
- ip = pack_ip_port(map, ip, port);
-
- if (!ip)
- return UINT_MAX;
-
- for (i = 0; i < map->probes; i++) {
- id = jhash_ip(map, i, ip) % map->hashsize;
- DP("hash key: %u", id);
- elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id);
- if (*elem == ip)
- return id;
- /* No shortcut - there can be deleted entries. */
- }
- return UINT_MAX;
+static inline void
+hash_ipport4_data_copy(struct hash_ipport4_elem *dst,
+ const struct hash_ipport4_elem *src)
+{
+ dst->ip = src->ip;
+ dst->port = src->port;
+ dst->match = 1;
}
-static inline int
-ipporthash_test(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port)
+static inline void
+hash_ipport4_data_swap(struct hash_ipport4_elem *dst,
+ struct hash_ipport4_elem *src)
{
- struct ip_set_ipporthash *map = set->data;
-
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
-
- return (ipporthash_id(set, ip, port) != UINT_MAX);
-}
-
-#define KADT_CONDITION \
- ip_set_ip_t port; \
- \
- if (flags[1] == 0) \
- return 0; \
- \
- port = get_port(skb, flags++); \
- \
- if (port == INVALID_PORT) \
- return 0;
-
-UADT(ipporthash, test, req->port)
-KADT(ipporthash, test, ipaddr, port)
-
-static inline int
-__ipporthash_add(struct ip_set_ipporthash *map, ip_set_ip_t *ip)
-{
- __u32 probe;
- u_int16_t i;
- ip_set_ip_t *elem, *slot = NULL;
-
- for (i = 0; i < map->probes; i++) {
- probe = jhash_ip(map, i, *ip) % map->hashsize;
- elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe);
- if (*elem == *ip)
- return -EEXIST;
- if (!(slot || *elem))
- slot = elem;
- /* There can be deleted entries, must check all slots */
+ swap(dst->ip, src->ip);
+ swap(dst->port, src->port);
+}
+
+static inline void
+hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem)
+{
+ elem->match = 0;
+}
+
+static inline bool
+hash_ipport4_data_list(struct sk_buff *skb,
+ const struct hash_ipport4_elem *data)
+{
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ return 0;
+
+nla_put_failure:
+ return 1;
+}
+
+static inline bool
+hash_ipport4_data_tlist(struct sk_buff *skb,
+ const struct hash_ipport4_elem *data)
+{
+ const struct hash_ipport4_telem *tdata =
+ (const struct hash_ipport4_telem *)data;
+
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(tdata->timeout)));
+
+ return 0;
+
+nla_put_failure:
+ return 1;
+}
+
+#define PF 4
+#define HOST_MASK 32
+#include <linux/netfilter/ip_set_chash.h>
+
+static int
+hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+{
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipport4_elem data = {};
+
+ ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
+ if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ return -EINVAL;
+
+ return adtfn(set, &data, GFP_ATOMIC, h->timeout);
+}
+
+static const struct nla_policy
+hash_ipport4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_U32 },
+ [IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
+{
+ struct chash *h = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ bool eexist = flags & IPSET_FLAG_EXIST;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipport4_elem data = {};
+ u32 timeout = h->timeout;
+ int ret;
+
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ hash_ipport4_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP])
+ data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_PORT])
+ data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout(h->timeout))
+ return -IPSET_ERR_TIMEOUT;
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- if (slot) {
- *slot = *ip;
- map->elements++;
- return 0;
+
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
+
+ if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
}
- /* Trigger rehashing */
- return -EAGAIN;
+ return ret;
}
-static inline int
-ipporthash_add(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port)
+static bool
+hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b)
{
- struct ip_set_ipporthash *map = set->data;
- if (map->elements > limit)
- return -ERANGE;
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
+ struct chash *x = a->data;
+ struct chash *y = b->data;
+
+ return x->maxelem == y->maxelem
+ && x->timeout == y->timeout
+ && x->htable_bits == y->htable_bits /* resizing ? */
+ && x->array_size == y->array_size
+ && x->chain_limit == y->chain_limit;
+}
- ip = pack_ip_port(map, ip, port);
+/* The type variant functions: IPv6 */
- if (!ip)
- return -ERANGE;
-
- return __ipporthash_add(map, &ip);
+struct hash_ipport6_elem {
+ union nf_inet_addr ip;
+ u16 port;
+ u16 match;
+};
+
+struct hash_ipport6_telem {
+ union nf_inet_addr ip;
+ u16 port;
+ u16 match;
+ unsigned long timeout;
+};
+
+static inline bool
+hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
+ const struct hash_ipport6_elem *ip2)
+{
+ return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
+ && ip1->port == ip2->port;
}
-UADT(ipporthash, add, req->port)
-KADT(ipporthash, add, ipaddr, port)
+static inline bool
+hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem)
+{
+ return elem->match == 0;
+}
static inline void
-__ipporthash_retry(struct ip_set_ipporthash *tmp,
- struct ip_set_ipporthash *map)
+hash_ipport6_data_copy(struct hash_ipport6_elem *dst,
+ const struct hash_ipport6_elem *src)
{
- tmp->first_ip = map->first_ip;
- tmp->last_ip = map->last_ip;
+ memcpy(dst, src, sizeof(*dst));
+ dst->match = 1;
}
-HASH_RETRY(ipporthash, ip_set_ip_t)
-
-static inline int
-ipporthash_del(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port)
+static inline void
+hash_ipport6_data_swap(struct hash_ipport6_elem *dst,
+ struct hash_ipport6_elem *src)
{
- struct ip_set_ipporthash *map = set->data;
- ip_set_ip_t id;
- ip_set_ip_t *elem;
+ struct hash_ipport6_elem tmp;
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
+ memcpy(&tmp, dst, sizeof(tmp));
+ memcpy(dst, src, sizeof(tmp));
+ memcpy(src, &tmp, sizeof(tmp));
+}
- id = ipporthash_id(set, ip, port);
+static inline void
+hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem)
+{
+ elem->match = 0;
+}
- if (id == UINT_MAX)
- return -EEXIST;
-
- elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id);
- *elem = 0;
- map->elements--;
+static inline bool
+hash_ipport6_data_list(struct sk_buff *skb,
+ const struct hash_ipport6_elem *data)
+{
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ return 0;
+nla_put_failure:
+ return 1;
+}
+
+static inline bool
+hash_ipport6_data_tlist(struct sk_buff *skb,
+ const struct hash_ipport6_elem *data)
+{
+ const struct hash_ipport6_telem *e =
+ (const struct hash_ipport6_telem *)data;
+
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(e->timeout)));
return 0;
+
+nla_put_failure:
+ return 1;
}
-UADT(ipporthash, del, req->port)
-KADT(ipporthash, del, ipaddr, port)
+#undef PF
+#undef HOST_MASK
+
+#define PF 6
+#define HOST_MASK 128
+#include <linux/netfilter/ip_set_chash.h>
+
+static int
+hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+{
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipport6_elem data = {};
+
+ ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ return -EINVAL;
+
+ return adtfn(set, &data, GFP_ATOMIC, h->timeout);
+}
-static inline int
-__ipporthash_create(const struct ip_set_req_ipporthash_create *req,
- struct ip_set_ipporthash *map)
+static const struct nla_policy
+hash_ipport6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
- if (req->to - req->from > MAX_RANGE) {
- ip_set_printk("range too big, %d elements (max %d)",
- req->to - req->from + 1, MAX_RANGE+1);
- return -ENOEXEC;
+ struct chash *h = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipport6_elem data = {};
+ u32 timeout = h->timeout;
+
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ hash_ipport6_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP])
+ memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
+ sizeof(struct in6_addr));
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_PORT])
+ data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout(h->timeout))
+ return -IPSET_ERR_TIMEOUT;
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- map->first_ip = req->from;
- map->last_ip = req->to;
- return 0;
+
+ return adtfn(set, &data, GFP_KERNEL, timeout);
}
-HASH_CREATE(ipporthash, ip_set_ip_t)
-HASH_DESTROY(ipporthash)
-HASH_FLUSH(ipporthash, ip_set_ip_t)
+/* Create hash:ip type of sets */
-static inline void
-__ipporthash_list_header(const struct ip_set_ipporthash *map,
- struct ip_set_req_ipporthash_create *header)
+static const struct nla_policy
+hash_ipport_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
+ [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
+ [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
+ [IPSET_ATTR_PROBES] = { .type = NLA_U8 },
+ [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
{
- header->from = map->first_ip;
- header->to = map->last_ip;
-}
+ struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+ struct chash *h;
-HASH_LIST_HEADER(ipporthash)
-HASH_LIST_MEMBERS_SIZE(ipporthash, ip_set_ip_t)
-HASH_LIST_MEMBERS(ipporthash, ip_set_ip_t)
+ if (!(set->family == AF_INET || set->family == AF_INET6))
+ return -IPSET_ERR_INVALID_FAMILY;
-IP_SET_RTYPE(ipporthash, IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE)
+ if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
+ hash_ipport_create_policy))
+ return -IPSET_ERR_PROTOCOL;
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-MODULE_DESCRIPTION("ipporthash type of IP sets");
-module_param(limit, int, 0600);
-MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets");
+ if (tb[IPSET_ATTR_HASHSIZE]) {
+ hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
+ if (hashsize < IPSET_MIMINAL_HASHSIZE)
+ hashsize = IPSET_MIMINAL_HASHSIZE;
+ }
+
+ if (tb[IPSET_ATTR_MAXELEM])
+ maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return -ENOMEM;
+
+ h->maxelem = maxelem;
+ h->htable_bits = htable_bits(hashsize);
+ h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
+ h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
+ get_random_bytes(&h->initval, sizeof(h->initval));
+ h->timeout = IPSET_NO_TIMEOUT;
+
+ h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL, &set->flags);
+ if (!h->htable) {
+ kfree(h);
+ return -ENOMEM;
+ }
+
+ set->data = h;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+
+ set->variant = set->family == AF_INET
+ ? &hash_ipport4_tvariant : &hash_ipport6_tvariant;
+
+ if (set->family == AF_INET)
+ hash_ipport4_gc_init(set);
+ else
+ hash_ipport6_gc_init(set);
+ } else {
+ set->variant = set->family == AF_INET
+ ? &hash_ipport4_variant : &hash_ipport6_variant;
+ }
+
+ pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
+ set->name, jhash_size(h->htable_bits),
+ h->htable_bits, h->maxelem, set->data, h->htable);
+
+ return 0;
+}
+
+static struct ip_set_type hash_ipport_type = {
+ .name = "hash:ip,port",
+ .protocol = IPSET_PROTOCOL,
+ .features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
+ .dimension = IPSET_DIM_TWO,
+ .family = AF_UNSPEC,
+ .revision = 0,
+ .create = hash_ipport_create,
+ .me = THIS_MODULE,
+};
+
+static int __init
+hash_ipport_init(void)
+{
+ return ip_set_type_register(&hash_ipport_type);
+}
+
+static void __exit
+hash_ipport_fini(void)
+{
+ ip_set_type_unregister(&hash_ipport_type);
+}
-REGISTER_MODULE(ipporthash)
+module_init(hash_ipport_init);
+module_exit(hash_ipport_fini);
diff --git a/kernel/ip_set_hash_ipportip.c b/kernel/ip_set_hash_ipportip.c
index 8b8f2a2..fbf2780 100644
--- a/kernel/ip_set_hash_ipportip.c
+++ b/kernel/ip_set_hash_ipportip.c
@@ -1,215 +1,485 @@
-/* Copyright (C) 2008 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-/* Kernel module implementing an ip+port+ip hash set */
+/* Kernel module implementing an IP set type: the hash:ip,port,ip type */
+#include <linux/netfilter/ip_set_kernel.h>
+#include <linux/netfilter/ip_set_jhash.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
#include <linux/skbuff.h>
-#include <linux/netfilter_ipv4/ip_set_jhash.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
-
#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+#include <net/pfxlen.h>
-#include <linux/netfilter_ipv4/ip_set_ipportiphash.h>
-#include <linux/netfilter_ipv4/ip_set_getport.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/ip_set.h>
+#include <linux/netfilter/ip_set_timeout.h>
+#include <linux/netfilter/ip_set_getport.h>
+#include <linux/netfilter/ip_set_hash.h>
-static int limit = MAX_RANGE;
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
+MODULE_DESCRIPTION("hash:ip,port,ip type of IP sets");
+MODULE_ALIAS("ip_set_hash:ip,port,ip");
+
+/* Type specific function prefix */
+#define TYPE hash_ipportip
+
+static bool
+hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b);
+
+#define hash_ipportip4_same_set hash_ipportip_same_set
+#define hash_ipportip6_same_set hash_ipportip_same_set
+
+/* The type variant functions: IPv4 */
+
+/* Member elements without timeout */
+struct hash_ipportip4_elem {
+ u32 ip;
+ u32 ip2;
+ u16 port;
+ u16 match;
+};
+
+/* Member elements with timeout support */
+struct hash_ipportip4_telem {
+ u32 ip;
+ u32 ip2;
+ u16 port;
+ u16 match;
+ unsigned long timeout;
+};
+
+static inline bool
+hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
+ const struct hash_ipportip4_elem *ip2)
+{
+ return ip1->ip == ip2->ip
+ && ip1->ip2 == ip2->ip2
+ && ip1->port == ip2->port;
+}
-#define jhash_ip2(map, i, ipport, ip1) \
- jhash_2words(ipport, ip1, *(map->initval + i))
+static inline bool
+hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem)
+{
+ return elem->match == 0;
+}
-static inline __u32
-ipportiphash_id(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1)
+static inline void
+hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst,
+ const struct hash_ipportip4_elem *src)
{
- struct ip_set_ipportiphash *map = set->data;
- __u32 id;
- u_int16_t i;
- struct ipportip *elem;
+ memcpy(dst, src, sizeof(*dst));
+ dst->match = 1;
+}
- ip = pack_ip_port(map, ip, port);
- if (!(ip || ip1))
- return UINT_MAX;
-
- for (i = 0; i < map->probes; i++) {
- id = jhash_ip2(map, i, ip, ip1) % map->hashsize;
- DP("hash key: %u", id);
- elem = HARRAY_ELEM(map->members, struct ipportip *, id);
- if (elem->ip == ip && elem->ip1 == ip1)
- return id;
- /* No shortcut - there can be deleted entries. */
- }
- return UINT_MAX;
+static inline void
+hash_ipportip4_data_swap(struct hash_ipportip4_elem *dst,
+ struct hash_ipportip4_elem *src)
+{
+ struct hash_ipportip4_elem tmp;
+
+ memcpy(&tmp, dst, sizeof(tmp));
+ memcpy(dst, src, sizeof(tmp));
+ memcpy(src, &tmp, sizeof(tmp));
}
-static inline int
-ipportiphash_test(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1)
+static inline void
+hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem)
{
- struct ip_set_ipportiphash *map = set->data;
+ elem->match = 0;
+}
+
+static inline bool
+hash_ipportip4_data_list(struct sk_buff *skb,
+ const struct hash_ipportip4_elem *data)
+{
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ return 0;
+
+nla_put_failure:
+ return 1;
+}
+
+static inline bool
+hash_ipportip4_data_tlist(struct sk_buff *skb,
+ const struct hash_ipportip4_elem *data)
+{
+ const struct hash_ipportip4_telem *tdata =
+ (const struct hash_ipportip4_telem *)data;
+
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(tdata->timeout)));
+
+ return 0;
+
+nla_put_failure:
+ return 1;
+}
+
+#define PF 4
+#define HOST_MASK 32
+#include <linux/netfilter/ip_set_chash.h>
+
+static int
+hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+{
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipportip4_elem data = {};
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
-
- return (ipportiphash_id(set, ip, port, ip1) != UINT_MAX);
-}
-
-#define KADT_CONDITION \
- ip_set_ip_t port, ip1; \
- \
- if (flags[2] == 0) \
- return 0; \
- \
- port = get_port(skb, flags++); \
- ip1 = ipaddr(skb, flags++); \
- \
- if (port == INVALID_PORT) \
- return 0;
-
-UADT(ipportiphash, test, req->port, req->ip1)
-KADT(ipportiphash, test, ipaddr, port, ip1)
-
-static inline int
-__ipportip_add(struct ip_set_ipportiphash *map,
- ip_set_ip_t ip, ip_set_ip_t ip1)
-{
- __u32 probe;
- u_int16_t i;
- struct ipportip *elem, *slot = NULL;
-
- for (i = 0; i < map->probes; i++) {
- probe = jhash_ip2(map, i, ip, ip1) % map->hashsize;
- elem = HARRAY_ELEM(map->members, struct ipportip *, probe);
- if (elem->ip == ip && elem->ip1 == ip1)
- return -EEXIST;
- if (!(slot || elem->ip || elem->ip1))
- slot = elem;
- /* There can be deleted entries, must check all slots */
+ ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
+ if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ return -EINVAL;
+ ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
+
+ return adtfn(set, &data, GFP_ATOMIC, h->timeout);
+}
+
+static const struct nla_policy
+hash_ipportip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_U32 },
+ [IPSET_ATTR_IP2] = { .type = NLA_U32 },
+ [IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
+{
+ struct chash *h = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ bool eexist = flags & IPSET_FLAG_EXIST;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipportip4_elem data = {};
+ u32 timeout = h->timeout;
+ int ret;
+
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ hash_ipportip4_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP])
+ data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP2])
+ data.ip2 = ip_set_get_n32(tb[IPSET_ATTR_IP2]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_PORT])
+ data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout(h->timeout))
+ return -IPSET_ERR_TIMEOUT;
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- if (slot) {
- slot->ip = ip;
- slot->ip1 = ip1;
- map->elements++;
- return 0;
+
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
+
+ if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
}
- /* Trigger rehashing */
- return -EAGAIN;
+ return ret;
}
-static inline int
-__ipportiphash_add(struct ip_set_ipportiphash *map,
- struct ipportip *elem)
+static bool
+hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b)
{
- return __ipportip_add(map, elem->ip, elem->ip1);
+ struct chash *x = a->data;
+ struct chash *y = b->data;
+
+ return x->maxelem == y->maxelem
+ && x->timeout == y->timeout
+ && x->htable_bits == y->htable_bits /* resizing ? */
+ && x->array_size == y->array_size
+ && x->chain_limit == y->chain_limit;
}
-static inline int
-ipportiphash_add(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1)
+/* The type variant functions: IPv6 */
+
+struct hash_ipportip6_elem {
+ union nf_inet_addr ip;
+ union nf_inet_addr ip2;
+ u16 port;
+ u16 match;
+};
+
+struct hash_ipportip6_telem {
+ union nf_inet_addr ip;
+ union nf_inet_addr ip2;
+ u16 port;
+ u16 match;
+ unsigned long timeout;
+};
+
+static inline bool
+hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
+ const struct hash_ipportip6_elem *ip2)
{
- struct ip_set_ipportiphash *map = set->data;
-
- if (map->elements > limit)
- return -ERANGE;
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
-
- ip = pack_ip_port(map, ip, port);
- if (!(ip || ip1))
- return -ERANGE;
-
- return __ipportip_add(map, ip, ip1);
+ return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
+ && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0
+ && ip1->port == ip2->port;
}
-UADT(ipportiphash, add, req->port, req->ip1)
-KADT(ipportiphash, add, ipaddr, port, ip1)
+static inline bool
+hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem)
+{
+ return elem->match == 0;
+}
static inline void
-__ipportiphash_retry(struct ip_set_ipportiphash *tmp,
- struct ip_set_ipportiphash *map)
+hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst,
+ const struct hash_ipportip6_elem *src)
{
- tmp->first_ip = map->first_ip;
- tmp->last_ip = map->last_ip;
+ memcpy(dst, src, sizeof(*dst));
+ dst->match = 1;
}
-HASH_RETRY2(ipportiphash, struct ipportip)
-
-static inline int
-ipportiphash_del(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1)
+static inline void
+hash_ipportip6_data_swap(struct hash_ipportip6_elem *dst,
+ struct hash_ipportip6_elem *src)
{
- struct ip_set_ipportiphash *map = set->data;
- ip_set_ip_t id;
- struct ipportip *elem;
+ struct hash_ipportip6_elem tmp;
+
+ memcpy(&tmp, dst, sizeof(tmp));
+ memcpy(dst, src, sizeof(tmp));
+ memcpy(src, &tmp, sizeof(tmp));
+}
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
+static inline void
+hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem)
+{
+ elem->match = 0;
+}
- id = ipportiphash_id(set, ip, port, ip1);
+static inline bool
+hash_ipportip6_data_list(struct sk_buff *skb,
+ const struct hash_ipportip6_elem *data)
+{
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
+ NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ return 0;
- if (id == UINT_MAX)
- return -EEXIST;
-
- elem = HARRAY_ELEM(map->members, struct ipportip *, id);
- elem->ip = elem->ip1 = 0;
- map->elements--;
+nla_put_failure:
+ return 1;
+}
+static inline bool
+hash_ipportip6_data_tlist(struct sk_buff *skb,
+ const struct hash_ipportip6_elem *data)
+{
+ const struct hash_ipportip6_telem *e =
+ (const struct hash_ipportip6_telem *)data;
+
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
+ NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(e->timeout)));
return 0;
+
+nla_put_failure:
+ return 1;
}
-UADT(ipportiphash, del, req->port, req->ip1)
-KADT(ipportiphash, del, ipaddr, port, ip1)
+#undef PF
+#undef HOST_MASK
+
+#define PF 6
+#define HOST_MASK 128
+#include <linux/netfilter/ip_set_chash.h>
+
+static int
+hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+{
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipportip6_elem data = {};
+
+ ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ return -EINVAL;
+ ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
+
+ return adtfn(set, &data, GFP_ATOMIC, h->timeout);
+}
-static inline int
-__ipportiphash_create(const struct ip_set_req_ipportiphash_create *req,
- struct ip_set_ipportiphash *map)
+static const struct nla_policy
+hash_ipportip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [IPSET_ATTR_IP2] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
- if (req->to - req->from > MAX_RANGE) {
- ip_set_printk("range too big, %d elements (max %d)",
- req->to - req->from + 1, MAX_RANGE+1);
- return -ENOEXEC;
+ struct chash *h = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipportip6_elem data = {};
+ u32 timeout = h->timeout;
+
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ hash_ipportip6_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP])
+ memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
+ sizeof(struct in6_addr));
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP2])
+ memcpy(&data.ip2, nla_data(tb[IPSET_ATTR_IP2]),
+ sizeof(struct in6_addr));
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_PORT])
+ data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout(h->timeout))
+ return -IPSET_ERR_TIMEOUT;
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- map->first_ip = req->from;
- map->last_ip = req->to;
- return 0;
+
+ return adtfn(set, &data, GFP_KERNEL, timeout);
}
-HASH_CREATE(ipportiphash, struct ipportip)
-HASH_DESTROY(ipportiphash)
-HASH_FLUSH(ipportiphash, struct ipportip)
+/* Create hash:ip type of sets */
-static inline void
-__ipportiphash_list_header(const struct ip_set_ipportiphash *map,
- struct ip_set_req_ipportiphash_create *header)
+static const struct nla_policy
+hash_ipportip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
+ [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
+ [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
+ [IPSET_ATTR_PROBES] = { .type = NLA_U8 },
+ [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_ipportip_create(struct ip_set *set, struct nlattr *head,
+ int len, u32 flags)
{
- header->from = map->first_ip;
- header->to = map->last_ip;
-}
+ struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+ struct chash *h;
-HASH_LIST_HEADER(ipportiphash)
-HASH_LIST_MEMBERS_SIZE(ipportiphash, struct ipportip)
-HASH_LIST_MEMBERS_MEMCPY(ipportiphash, struct ipportip,
- (elem->ip || elem->ip1))
+ if (!(set->family == AF_INET || set->family == AF_INET6))
+ return -IPSET_ERR_INVALID_FAMILY;
-IP_SET_RTYPE(ipportiphash, IPSET_TYPE_IP | IPSET_TYPE_PORT
- | IPSET_TYPE_IP1 | IPSET_DATA_TRIPLE)
+ if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
+ hash_ipportip_create_policy))
+ return -IPSET_ERR_PROTOCOL;
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-MODULE_DESCRIPTION("ipportiphash type of IP sets");
-module_param(limit, int, 0600);
-MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets");
+ if (tb[IPSET_ATTR_HASHSIZE]) {
+ hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
+ if (hashsize < IPSET_MIMINAL_HASHSIZE)
+ hashsize = IPSET_MIMINAL_HASHSIZE;
+ }
+
+ if (tb[IPSET_ATTR_MAXELEM])
+ maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return -ENOMEM;
+
+ h->maxelem = maxelem;
+ h->htable_bits = htable_bits(hashsize);
+ h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
+ h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
+ get_random_bytes(&h->initval, sizeof(h->initval));
+ h->timeout = IPSET_NO_TIMEOUT;
+
+ h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL, &set->flags);
+ if (!h->htable) {
+ kfree(h);
+ return -ENOMEM;
+ }
+
+ set->data = h;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+
+ set->variant = set->family == AF_INET
+ ? &hash_ipportip4_tvariant : &hash_ipportip6_tvariant;
+
+ if (set->family == AF_INET)
+ hash_ipportip4_gc_init(set);
+ else
+ hash_ipportip6_gc_init(set);
+ } else {
+ set->variant = set->family == AF_INET
+ ? &hash_ipportip4_variant : &hash_ipportip6_variant;
+ }
+
+ pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
+ set->name, jhash_size(h->htable_bits),
+ h->htable_bits, h->maxelem, set->data, h->htable);
+
+ return 0;
+}
+
+static struct ip_set_type hash_ipportip_type = {
+ .name = "hash:ip,port,ip",
+ .protocol = IPSET_PROTOCOL,
+ .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
+ .dimension = IPSET_DIM_THREE,
+ .family = AF_UNSPEC,
+ .revision = 0,
+ .create = hash_ipportip_create,
+ .me = THIS_MODULE,
+};
+
+static int __init
+hash_ipportip_init(void)
+{
+ return ip_set_type_register(&hash_ipportip_type);
+}
+
+static void __exit
+hash_ipportip_fini(void)
+{
+ ip_set_type_unregister(&hash_ipportip_type);
+}
-REGISTER_MODULE(ipportiphash)
+module_init(hash_ipportip_init);
+module_exit(hash_ipportip_fini);
diff --git a/kernel/ip_set_hash_ipportnet.c b/kernel/ip_set_hash_ipportnet.c
index e0bb352..dfe9348 100644
--- a/kernel/ip_set_hash_ipportnet.c
+++ b/kernel/ip_set_hash_ipportnet.c
@@ -1,298 +1,554 @@
-/* Copyright (C) 2008 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-/* Kernel module implementing an ip+port+net hash set */
+/* Kernel module implementing an IP set type: the hash:ip,port,net type */
+#include <linux/netfilter/ip_set_kernel.h>
+#include <linux/netfilter/ip_set_jhash.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
#include <linux/skbuff.h>
-#include <linux/netfilter_ipv4/ip_set_jhash.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
-
#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+#include <net/pfxlen.h>
-#include <linux/netfilter_ipv4/ip_set_ipportnethash.h>
-#include <linux/netfilter_ipv4/ip_set_getport.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/ip_set.h>
+#include <linux/netfilter/ip_set_timeout.h>
+#include <linux/netfilter/ip_set_getport.h>
+#include <linux/netfilter/ip_set_hash.h>
-static int limit = MAX_RANGE;
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
+MODULE_DESCRIPTION("hash:ip,port,net type of IP sets");
+MODULE_ALIAS("ip_set_hash:ip,port,net");
+
+/* Type specific function prefix */
+#define TYPE hash_ipportnet
+
+static bool
+hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b);
+
+#define hash_ipportnet4_same_set hash_ipportnet_same_set
+#define hash_ipportnet6_same_set hash_ipportnet_same_set
+
+/* The type variant functions: IPv4 */
+
+/* Member elements without timeout */
+struct hash_ipportnet4_elem {
+ u32 ip;
+ u32 ip2;
+ u16 port;
+ u8 cidr;
+ u8 match;
+};
+
+/* Member elements with timeout support */
+struct hash_ipportnet4_telem {
+ u32 ip;
+ u32 ip2;
+ u16 port;
+ u8 cidr;
+ u8 match;
+ unsigned long timeout;
+};
+
+static inline bool
+hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
+ const struct hash_ipportnet4_elem *ip2)
+{
+ return ip1->ip == ip2->ip
+ && ip1->ip2 == ip2->ip2
+ && ip1->cidr == ip2->cidr
+ && ip1->port == ip2->port;
+}
-#define jhash_ip2(map, i, ipport, ip1) \
- jhash_2words(ipport, ip1, *(map->initval + i))
+static inline bool
+hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem)
+{
+ return elem->match == 0;
+}
-static inline __u32
-ipportnethash_id_cidr(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port,
- ip_set_ip_t ip1, uint8_t cidr)
+static inline void
+hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
+ const struct hash_ipportnet4_elem *src)
{
- struct ip_set_ipportnethash *map = set->data;
- __u32 id;
- u_int16_t i;
- struct ipportip *elem;
-
- ip = pack_ip_port(map, ip, port);
- ip1 = pack_ip_cidr(ip1, cidr);
- if (!(ip || ip1))
- return UINT_MAX;
-
- for (i = 0; i < map->probes; i++) {
- id = jhash_ip2(map, i, ip, ip1) % map->hashsize;
- DP("hash key: %u", id);
- elem = HARRAY_ELEM(map->members, struct ipportip *, id);
- if (elem->ip == ip && elem->ip1 == ip1)
- return id;
- /* No shortcut - there can be deleted entries. */
- }
- return UINT_MAX;
+ memcpy(dst, src, sizeof(*dst));
+ dst->match = 1;
}
-static inline __u32
-ipportnethash_id(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1)
+static inline void
+hash_ipportnet4_data_swap(struct hash_ipportnet4_elem *dst,
+ struct hash_ipportnet4_elem *src)
{
- struct ip_set_ipportnethash *map = set->data;
- __u32 id = UINT_MAX;
- int i;
-
- for (i = 0; i < 30 && map->cidr[i]; i++) {
- id = ipportnethash_id_cidr(set, ip, port, ip1, map->cidr[i]);
- if (id != UINT_MAX)
- break;
- }
- return id;
+ struct hash_ipportnet4_elem tmp;
+
+ memcpy(&tmp, dst, sizeof(tmp));
+ memcpy(dst, src, sizeof(tmp));
+ memcpy(src, &tmp, sizeof(tmp));
+}
+
+static inline void
+hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
+{
+ elem->ip2 &= NETMASK(cidr);
+ elem->cidr = cidr;
}
-static inline int
-ipportnethash_test_cidr(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port,
- ip_set_ip_t ip1, uint8_t cidr)
+static inline void
+hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem)
{
- struct ip_set_ipportnethash *map = set->data;
-
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
+ elem->match = 0;
+}
+
+static inline bool
+hash_ipportnet4_data_list(struct sk_buff *skb,
+ const struct hash_ipportnet4_elem *data)
+{
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ return 0;
- return (ipportnethash_id_cidr(set, ip, port, ip1, cidr) != UINT_MAX);
+nla_put_failure:
+ return 1;
}
-static inline int
-ipportnethash_test(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1)
+static inline bool
+hash_ipportnet4_data_tlist(struct sk_buff *skb,
+ const struct hash_ipportnet4_elem *data)
{
- struct ip_set_ipportnethash *map = set->data;
-
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
+ const struct hash_ipportnet4_telem *tdata =
+ (const struct hash_ipportnet4_telem *)data;
+
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(tdata->timeout)));
- return (ipportnethash_id(set, ip, port, ip1) != UINT_MAX);
+ return 0;
+
+nla_put_failure:
+ return 1;
}
+#define IP_SET_HASH_WITH_NETS
+
+#define PF 4
+#define HOST_MASK 32
+#include <linux/netfilter/ip_set_chash.h>
+
static int
-ipportnethash_utest(struct ip_set *set, const void *data, u_int32_t size)
+hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
- const struct ip_set_req_ipportnethash *req = data;
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipportnet4_elem data =
+ { .cidr = h->nets[0].cidr || HOST_MASK };
+
+ if (data.cidr == 0)
+ return -EINVAL;
+ if (adt == IPSET_TEST)
+ data.cidr = HOST_MASK;
- if (req->cidr <= 0 || req->cidr > 32)
+ ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
+ if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
return -EINVAL;
- return (req->cidr == 32
- ? ipportnethash_test(set, req->ip, req->port, req->ip1)
- : ipportnethash_test_cidr(set, req->ip, req->port,
- req->ip1, req->cidr));
+ ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
+ data.ip2 &= NETMASK(data.cidr);
+
+ return adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
-#define KADT_CONDITION \
- ip_set_ip_t port, ip1; \
- \
- if (flags[2] == 0) \
- return 0; \
- \
- port = get_port(skb, flags++); \
- ip1 = ipaddr(skb, flags++); \
- \
- if (port == INVALID_PORT) \
- return 0;
-
-KADT(ipportnethash, test, ipaddr, port, ip1)
-
-static inline int
-__ipportnet_add(struct ip_set_ipportnethash *map,
- ip_set_ip_t ip, ip_set_ip_t ip1)
+static const struct nla_policy
+hash_ipportnet4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_U32 },
+ [IPSET_ATTR_IP2] = { .type = NLA_U32 },
+ [IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
- __u32 probe;
- u_int16_t i;
- struct ipportip *elem, *slot = NULL;
-
- for (i = 0; i < map->probes; i++) {
- probe = jhash_ip2(map, i, ip, ip1) % map->hashsize;
- elem = HARRAY_ELEM(map->members, struct ipportip *, probe);
- if (elem->ip == ip && elem->ip1 == ip1)
- return -EEXIST;
- if (!(slot || elem->ip || elem->ip1))
- slot = elem;
- /* There can be deleted entries, must check all slots */
+ struct chash *h = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ bool eexist = flags & IPSET_FLAG_EXIST;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
+ u32 timeout = h->timeout;
+ int ret;
+
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ hash_ipportnet4_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP])
+ data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP2])
+ data.ip2 = ip_set_get_n32(tb[IPSET_ATTR_IP2]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_CIDR2])
+ data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+
+ if (!data.cidr)
+ return -IPSET_ERR_INVALID_CIDR;
+
+ data.ip2 &= NETMASK(data.cidr);
+
+ if (tb[IPSET_ATTR_PORT])
+ data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout(h->timeout))
+ return -IPSET_ERR_TIMEOUT;
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- if (slot) {
- slot->ip = ip;
- slot->ip1 = ip1;
- map->elements++;
- return 0;
+
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
+
+ if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
}
- /* Trigger rehashing */
- return -EAGAIN;
+ return ret;
}
-static inline int
-__ipportnethash_add(struct ip_set_ipportnethash *map,
- struct ipportip *elem)
+static bool
+hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b)
{
- return __ipportnet_add(map, elem->ip, elem->ip1);
+ struct chash *x = a->data;
+ struct chash *y = b->data;
+
+ return x->maxelem == y->maxelem
+ && x->timeout == y->timeout
+ && x->htable_bits == y->htable_bits /* resizing ? */
+ && x->array_size == y->array_size
+ && x->chain_limit == y->chain_limit;
}
-static inline int
-ipportnethash_add(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port,
- ip_set_ip_t ip1, uint8_t cidr)
+/* The type variant functions: IPv6 */
+
+struct hash_ipportnet6_elem {
+ union nf_inet_addr ip;
+ union nf_inet_addr ip2;
+ u16 port;
+ u8 cidr;
+ u8 match;
+};
+
+struct hash_ipportnet6_telem {
+ union nf_inet_addr ip;
+ union nf_inet_addr ip2;
+ u16 port;
+ u8 cidr;
+ u8 match;
+ unsigned long timeout;
+};
+
+static inline bool
+hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
+ const struct hash_ipportnet6_elem *ip2)
{
- struct ip_set_ipportnethash *map = set->data;
- struct ipportip;
- int ret;
-
- if (map->elements > limit)
- return -ERANGE;
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
- if (cidr <= 0 || cidr >= 32)
- return -EINVAL;
- if (map->nets[cidr-1] == UINT16_MAX)
- return -ERANGE;
+ return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
+ && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0
+ && ip1->cidr == ip2->cidr
+ && ip1->port == ip2->port;
+}
- ip = pack_ip_port(map, ip, port);
- ip1 = pack_ip_cidr(ip1, cidr);
- if (!(ip || ip1))
- return -ERANGE;
-
- ret =__ipportnet_add(map, ip, ip1);
- if (ret == 0) {
- if (!map->nets[cidr-1]++)
- add_cidr_size(map->cidr, cidr);
- }
- return ret;
+static inline bool
+hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem)
+{
+ return elem->match == 0;
}
-#undef KADT_CONDITION
-#define KADT_CONDITION \
- struct ip_set_ipportnethash *map = set->data; \
- uint8_t cidr = map->cidr[0] ? map->cidr[0] : 31; \
- ip_set_ip_t port, ip1; \
- \
- if (flags[2] == 0) \
- return 0; \
- \
- port = get_port(skb, flags++); \
- ip1 = ipaddr(skb, flags++); \
- \
- if (port == INVALID_PORT) \
- return 0;
-
-UADT(ipportnethash, add, req->port, req->ip1, req->cidr)
-KADT(ipportnethash, add, ipaddr, port, ip1, cidr)
+static inline void
+hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
+ const struct hash_ipportnet6_elem *src)
+{
+ memcpy(dst, src, sizeof(*dst));
+ dst->match = 1;
+}
static inline void
-__ipportnethash_retry(struct ip_set_ipportnethash *tmp,
- struct ip_set_ipportnethash *map)
+hash_ipportnet6_data_swap(struct hash_ipportnet6_elem *dst,
+ struct hash_ipportnet6_elem *src)
{
- tmp->first_ip = map->first_ip;
- tmp->last_ip = map->last_ip;
- memcpy(tmp->cidr, map->cidr, sizeof(tmp->cidr));
- memcpy(tmp->nets, map->nets, sizeof(tmp->nets));
+ struct hash_ipportnet6_elem tmp;
+
+ memcpy(&tmp, dst, sizeof(tmp));
+ memcpy(dst, src, sizeof(tmp));
+ memcpy(src, &tmp, sizeof(tmp));
}
-HASH_RETRY2(ipportnethash, struct ipportip)
+static inline void
+hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
+{
+ elem->match = 0;
+}
-static inline int
-ipportnethash_del(struct ip_set *set,
- ip_set_ip_t ip, ip_set_ip_t port,
- ip_set_ip_t ip1, uint8_t cidr)
+static inline void
+ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
- struct ip_set_ipportnethash *map = set->data;
- ip_set_ip_t id;
- struct ipportip *elem;
+ ip->ip6[0] &= NETMASK6(prefix)[0];
+ ip->ip6[1] &= NETMASK6(prefix)[1];
+ ip->ip6[2] &= NETMASK6(prefix)[2];
+ ip->ip6[3] &= NETMASK6(prefix)[3];
+}
- if (ip < map->first_ip || ip > map->last_ip)
- return -ERANGE;
- if (!ip)
- return -ERANGE;
- if (cidr <= 0 || cidr >= 32)
- return -EINVAL;
+static inline void
+hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
+{
+ ip6_netmask(&elem->ip2, cidr);
+ elem->cidr = cidr;
+}
- id = ipportnethash_id_cidr(set, ip, port, ip1, cidr);
+static inline bool
+hash_ipportnet6_data_list(struct sk_buff *skb,
+ const struct hash_ipportnet6_elem *data)
+{
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
+ NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ return 0;
- if (id == UINT_MAX)
- return -EEXIST;
-
- elem = HARRAY_ELEM(map->members, struct ipportip *, id);
- elem->ip = elem->ip1 = 0;
- map->elements--;
- if (!map->nets[cidr-1]--)
- del_cidr_size(map->cidr, cidr);
+nla_put_failure:
+ return 1;
+}
+static inline bool
+hash_ipportnet6_data_tlist(struct sk_buff *skb,
+ const struct hash_ipportnet6_elem *data)
+{
+ const struct hash_ipportnet6_telem *e =
+ (const struct hash_ipportnet6_telem *)data;
+
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
+ NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
+ NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(e->timeout)));
return 0;
+
+nla_put_failure:
+ return 1;
}
-UADT(ipportnethash, del, req->port, req->ip1, req->cidr)
-KADT(ipportnethash, del, ipaddr, port, ip1, cidr)
+#undef PF
+#undef HOST_MASK
+
+#define PF 6
+#define HOST_MASK 128
+#include <linux/netfilter/ip_set_chash.h>
-static inline int
-__ipportnethash_create(const struct ip_set_req_ipportnethash_create *req,
- struct ip_set_ipportnethash *map)
+static int
+hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
- if (req->to - req->from > MAX_RANGE) {
- ip_set_printk("range too big, %d elements (max %d)",
- req->to - req->from + 1, MAX_RANGE+1);
- return -ENOEXEC;
- }
- map->first_ip = req->from;
- map->last_ip = req->to;
- memset(map->cidr, 0, sizeof(map->cidr));
- memset(map->nets, 0, sizeof(map->nets));
- return 0;
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipportnet6_elem data =
+ { .cidr = h->nets[0].cidr || HOST_MASK };
+
+ if (data.cidr == 0)
+ return -EINVAL;
+ if (adt == IPSET_TEST)
+ data.cidr = HOST_MASK;
+
+ ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ return -EINVAL;
+ ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
+ ip6_netmask(&data.ip2, data.cidr);
+
+ return adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
-HASH_CREATE(ipportnethash, struct ipportip)
-HASH_DESTROY(ipportnethash)
-HASH_FLUSH_CIDR(ipportnethash, struct ipportip);
+static const struct nla_policy
+hash_ipportnet6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [IPSET_ATTR_IP2] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
-static inline void
-__ipportnethash_list_header(const struct ip_set_ipportnethash *map,
- struct ip_set_req_ipportnethash_create *header)
+static int
+hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
- header->from = map->first_ip;
- header->to = map->last_ip;
+ struct chash *h = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
+ u32 timeout = h->timeout;
+
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ hash_ipportnet6_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP])
+ memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
+ sizeof(struct in6_addr));
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP2])
+ memcpy(&data.ip2, nla_data(tb[IPSET_ATTR_IP2]),
+ sizeof(struct in6_addr));
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_CIDR2])
+ data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+
+ if (!data.cidr)
+ return -IPSET_ERR_INVALID_CIDR;
+
+ ip6_netmask(&data.ip2, data.cidr);
+
+ if (tb[IPSET_ATTR_PORT])
+ data.port = ip_set_get_n16(tb[IPSET_ATTR_PORT]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout(h->timeout))
+ return -IPSET_ERR_TIMEOUT;
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+ }
+
+ return adtfn(set, &data, GFP_KERNEL, timeout);
}
-HASH_LIST_HEADER(ipportnethash)
+/* Create hash:ip type of sets */
+
+static const struct nla_policy
+hash_ipportnet_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
+ [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
+ [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
+ [IPSET_ATTR_PROBES] = { .type = NLA_U8 },
+ [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
+ int len, u32 flags)
+{
+ struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+ struct chash *h;
-HASH_LIST_MEMBERS_SIZE(ipportnethash, struct ipportip)
-HASH_LIST_MEMBERS_MEMCPY(ipportnethash, struct ipportip,
- (elem->ip || elem->ip1))
+ if (!(set->family == AF_INET || set->family == AF_INET6))
+ return -IPSET_ERR_INVALID_FAMILY;
-IP_SET_RTYPE(ipportnethash, IPSET_TYPE_IP | IPSET_TYPE_PORT
- | IPSET_TYPE_IP1 | IPSET_DATA_TRIPLE)
+ if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
+ hash_ipportnet_create_policy))
+ return -IPSET_ERR_PROTOCOL;
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-MODULE_DESCRIPTION("ipportnethash type of IP sets");
-module_param(limit, int, 0600);
-MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets");
+ if (tb[IPSET_ATTR_HASHSIZE]) {
+ hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
+ if (hashsize < IPSET_MIMINAL_HASHSIZE)
+ hashsize = IPSET_MIMINAL_HASHSIZE;
+ }
+
+ if (tb[IPSET_ATTR_MAXELEM])
+ maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
+
+ h = kzalloc(sizeof(*h)
+ + sizeof(struct chash_nets)
+ * (set->family == AF_INET ? 31 : 127), GFP_KERNEL);
+ if (!h)
+ return -ENOMEM;
+
+ h->maxelem = maxelem;
+ h->htable_bits = htable_bits(hashsize);
+ h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
+ h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
+ get_random_bytes(&h->initval, sizeof(h->initval));
+ h->timeout = IPSET_NO_TIMEOUT;
+
+ h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL, &set->flags);
+ if (!h->htable) {
+ kfree(h);
+ return -ENOMEM;
+ }
+
+ set->data = h;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+
+ set->variant = set->family == AF_INET
+ ? &hash_ipportnet4_tvariant : &hash_ipportnet6_tvariant;
+
+ if (set->family == AF_INET)
+ hash_ipportnet4_gc_init(set);
+ else
+ hash_ipportnet6_gc_init(set);
+ } else {
+ set->variant = set->family == AF_INET
+ ? &hash_ipportnet4_variant : &hash_ipportnet6_variant;
+ }
+
+ pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
+ set->name, jhash_size(h->htable_bits),
+ h->htable_bits, h->maxelem, set->data, h->htable);
+
+ return 0;
+}
+
+static struct ip_set_type hash_ipportnet_type = {
+ .name = "hash:ip,port,net",
+ .protocol = IPSET_PROTOCOL,
+ .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
+ .dimension = IPSET_DIM_THREE,
+ .family = AF_UNSPEC,
+ .revision = 0,
+ .create = hash_ipportnet_create,
+ .me = THIS_MODULE,
+};
+
+static int __init
+hash_ipportnet_init(void)
+{
+ return ip_set_type_register(&hash_ipportnet_type);
+}
+
+static void __exit
+hash_ipportnet_fini(void)
+{
+ ip_set_type_unregister(&hash_ipportnet_type);
+}
-REGISTER_MODULE(ipportnethash)
+module_init(hash_ipportnet_init);
+module_exit(hash_ipportnet_fini);
diff --git a/kernel/ip_set_hash_net.c b/kernel/ip_set_hash_net.c
index e3b09e0..a8611c2 100644
--- a/kernel/ip_set_hash_net.c
+++ b/kernel/ip_set_hash_net.c
@@ -1,218 +1,488 @@
-/* Copyright (C) 2003-2008 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-/* Kernel module implementing a cidr nethash set */
+/* Kernel module implementing an IP set type: the hash:net type */
+#include <linux/netfilter/ip_set_kernel.h>
+#include <linux/netfilter/ip_set_jhash.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
-#include <linux/netfilter_ipv4/ip_set_jhash.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/random.h>
-
#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+#include <net/pfxlen.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/ip_set.h>
+#include <linux/netfilter/ip_set_timeout.h>
+#include <linux/netfilter/ip_set_hash.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
+MODULE_DESCRIPTION("hash:net type of IP sets");
+MODULE_ALIAS("ip_set_hash:net");
+
+/* Type specific function prefix */
+#define TYPE hash_net
+
+static bool
+hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
+
+#define hash_net4_same_set hash_net_same_set
+#define hash_net6_same_set hash_net_same_set
-#include <linux/netfilter_ipv4/ip_set_nethash.h>
+/* The type variant functions: IPv4 */
-static int limit = MAX_RANGE;
+/* Member elements without timeout */
+struct hash_net4_elem {
+ u32 ip;
+ u8 cidr; /* Not hashed, zero for null value */
+};
-static inline __u32
-nethash_id_cidr(const struct ip_set_nethash *map,
- ip_set_ip_t ip,
- uint8_t cidr)
+/* Member elements with timeout support */
+struct hash_net4_telem {
+ u32 ip;
+ u8 cidr; /* Not hashed, zero for null value */
+ unsigned long timeout;
+};
+
+static inline bool
+hash_net4_data_equal(const struct hash_net4_elem *ip1,
+ const struct hash_net4_elem *ip2)
{
- __u32 id;
- u_int16_t i;
- ip_set_ip_t *elem;
+ /* We don't have to check the cidr equality
+ * because overlapping nets cannot be added to the set
+ */
+ return ip1->ip == ip2->ip;
+}
- ip = pack_ip_cidr(ip, cidr);
- if (!ip)
- return MAX_RANGE;
-
- for (i = 0; i < map->probes; i++) {
- id = jhash_ip(map, i, ip) % map->hashsize;
- DP("hash key: %u", id);
- elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id);
- if (*elem == ip)
- return id;
- /* No shortcut - there can be deleted entries. */
- }
- return UINT_MAX;
+static inline bool
+hash_net4_data_isnull(const struct hash_net4_elem *elem)
+{
+ return elem->cidr == 0;
}
-static inline __u32
-nethash_id(struct ip_set *set, ip_set_ip_t ip)
+static inline void
+hash_net4_data_copy(struct hash_net4_elem *dst,
+ const struct hash_net4_elem *src)
{
- const struct ip_set_nethash *map = set->data;
- __u32 id = UINT_MAX;
- int i;
+ dst->ip = src->ip;
+ dst->cidr = src->cidr;
+}
- for (i = 0; i < 30 && map->cidr[i]; i++) {
- id = nethash_id_cidr(map, ip, map->cidr[i]);
- if (id != UINT_MAX)
- break;
- }
- return id;
+static inline void
+hash_net4_data_swap(struct hash_net4_elem *dst,
+ struct hash_net4_elem *src)
+{
+ swap(dst->ip, src->ip);
+ swap(dst->cidr, src->cidr);
}
-static inline int
-nethash_test_cidr(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr)
+static inline void
+hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr)
{
- const struct ip_set_nethash *map = set->data;
+ elem->ip &= NETMASK(cidr);
+ elem->cidr = cidr;
+}
- return (nethash_id_cidr(map, ip, cidr) != UINT_MAX);
+/* Zero CIDR values cannot be stored */
+static inline void
+hash_net4_data_zero_out(struct hash_net4_elem *elem)
+{
+ elem->cidr = 0;
}
-static inline int
-nethash_test(struct ip_set *set, ip_set_ip_t ip)
+static inline bool
+hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
{
- return (nethash_id(set, ip) != UINT_MAX);
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+ return 0;
+
+nla_put_failure:
+ return 1;
}
-static int
-nethash_utest(struct ip_set *set, const void *data, u_int32_t size)
+static inline bool
+hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
{
- const struct ip_set_req_nethash *req = data;
+ const struct hash_net4_telem *tdata =
+ (const struct hash_net4_telem *)data;
- if (req->cidr <= 0 || req->cidr > 32)
- return -EINVAL;
- return (req->cidr == 32 ? nethash_test(set, req->ip)
- : nethash_test_cidr(set, req->ip, req->cidr));
+ NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(tdata->timeout)));
+
+ return 0;
+
+nla_put_failure:
+ return 1;
}
-#define KADT_CONDITION
+#define IP_SET_HASH_WITH_NETS
-KADT(nethash, test, ipaddr)
+#define PF 4
+#define HOST_MASK 32
+#include <linux/netfilter/ip_set_chash.h>
-static inline int
-__nethash_add(struct ip_set_nethash *map, ip_set_ip_t *ip)
+static int
+hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
- __u32 probe;
- u_int16_t i;
- ip_set_ip_t *elem, *slot = NULL;
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_net4_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
- for (i = 0; i < map->probes; i++) {
- probe = jhash_ip(map, i, *ip) % map->hashsize;
- elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe);
- if (*elem == *ip)
- return -EEXIST;
- if (!(slot || *elem))
- slot = elem;
- /* There can be deleted entries, must check all slots */
- }
- if (slot) {
- *slot = *ip;
- map->elements++;
- return 0;
- }
- /* Trigger rehashing */
- return -EAGAIN;
+ if (data.cidr == 0)
+ return -EINVAL;
+ if (adt == IPSET_TEST)
+ data.cidr = HOST_MASK;
+
+ ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
+ data.ip &= NETMASK(data.cidr);
+
+ return adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
-static inline int
-nethash_add(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr)
+static const struct nla_policy
+hash_net4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_U32 },
+ [IPSET_ATTR_CIDR] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
- struct ip_set_nethash *map = set->data;
+ struct chash *h = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ bool eexist = flags & IPSET_FLAG_EXIST;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_net4_elem data = { .cidr = HOST_MASK };
+ u32 timeout = h->timeout;
int ret;
-
- if (map->elements >= limit || map->nets[cidr-1] == UINT16_MAX)
- return -ERANGE;
- if (cidr <= 0 || cidr >= 32)
- return -EINVAL;
- ip = pack_ip_cidr(ip, cidr);
- if (!ip)
- return -ERANGE;
-
- ret = __nethash_add(map, &ip);
- if (ret == 0) {
- if (!map->nets[cidr-1]++)
- add_cidr_size(map->cidr, cidr);
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ hash_net4_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP])
+ data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_CIDR])
+ data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+
+ if (!data.cidr)
+ return -IPSET_ERR_INVALID_CIDR;
+
+ data.ip &= NETMASK(data.cidr);
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout(h->timeout))
+ return -IPSET_ERR_TIMEOUT;
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+ }
+
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
+
+ if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
}
-
return ret;
}
-#undef KADT_CONDITION
-#define KADT_CONDITION \
- struct ip_set_nethash *map = set->data; \
- uint8_t cidr = map->cidr[0] ? map->cidr[0] : 31;
+static bool
+hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
+{
+ struct chash *x = a->data;
+ struct chash *y = b->data;
+
+ return x->maxelem == y->maxelem
+ && x->timeout == y->timeout
+ && x->htable_bits == y->htable_bits /* resizing ? */
+ && x->array_size == y->array_size
+ && x->chain_limit == y->chain_limit;
+}
+
+/* The type variant functions: IPv6 */
+
+struct hash_net6_elem {
+ union nf_inet_addr ip;
+ u8 cidr; /* Not hashed */
+};
-UADT(nethash, add, req->cidr)
-KADT(nethash, add, ipaddr, cidr)
+struct hash_net6_telem {
+ union nf_inet_addr ip;
+ u8 cidr; /* Not hashed */
+ unsigned long timeout;
+};
+
+static inline bool
+hash_net6_data_equal(const struct hash_net6_elem *ip1,
+ const struct hash_net6_elem *ip2)
+{
+ return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0;
+}
+
+static inline bool
+hash_net6_data_isnull(const struct hash_net6_elem *elem)
+{
+ return elem->cidr == 0;
+}
static inline void
-__nethash_retry(struct ip_set_nethash *tmp, struct ip_set_nethash *map)
+hash_net6_data_copy(struct hash_net6_elem *dst,
+ const struct hash_net6_elem *src)
{
- memcpy(tmp->cidr, map->cidr, sizeof(tmp->cidr));
- memcpy(tmp->nets, map->nets, sizeof(tmp->nets));
+ ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
+ dst->cidr = src->cidr;
}
-HASH_RETRY(nethash, ip_set_ip_t)
+static inline void
+hash_net6_data_swap(struct hash_net6_elem *dst, struct hash_net6_elem *src)
+{
+ struct hash_net6_elem tmp;
+
+ memcpy(&tmp, dst, sizeof(tmp));
+ memcpy(dst, src, sizeof(tmp));
+ memcpy(src, &tmp, sizeof(tmp));
+}
-static inline int
-nethash_del(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr)
+static inline void
+hash_net6_data_zero_out(struct hash_net6_elem *elem)
{
- struct ip_set_nethash *map = set->data;
- ip_set_ip_t id, *elem;
+ elem->cidr = 0;
+}
- if (cidr <= 0 || cidr >= 32)
- return -EINVAL;
-
- id = nethash_id_cidr(map, ip, cidr);
- if (id == UINT_MAX)
- return -EEXIST;
-
- elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id);
- *elem = 0;
- map->elements--;
- if (!map->nets[cidr-1]--)
- del_cidr_size(map->cidr, cidr);
- return 0;
+static inline void
+ip6_netmask(union nf_inet_addr *ip, u8 prefix)
+{
+ ip->ip6[0] &= NETMASK6(prefix)[0];
+ ip->ip6[1] &= NETMASK6(prefix)[1];
+ ip->ip6[2] &= NETMASK6(prefix)[2];
+ ip->ip6[3] &= NETMASK6(prefix)[3];
}
-UADT(nethash, del, req->cidr)
-KADT(nethash, del, ipaddr, cidr)
+static inline void
+hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
+{
+ ip6_netmask(&elem->ip, cidr);
+ elem->cidr = cidr;
+}
-static inline int
-__nethash_create(const struct ip_set_req_nethash_create *req,
- struct ip_set_nethash *map)
+static inline bool
+hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data)
{
- memset(map->cidr, 0, sizeof(map->cidr));
- memset(map->nets, 0, sizeof(map->nets));
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+ return 0;
+
+nla_put_failure:
+ return 1;
+}
+
+static inline bool
+hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
+{
+ const struct hash_net6_telem *e =
+ (const struct hash_net6_telem *)data;
+ NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr);
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(e->timeout)));
return 0;
+
+nla_put_failure:
+ return 1;
}
-HASH_CREATE(nethash, ip_set_ip_t)
-HASH_DESTROY(nethash)
+#undef PF
+#undef HOST_MASK
-HASH_FLUSH_CIDR(nethash, ip_set_ip_t)
+#define PF 6
+#define HOST_MASK 128
+#include <linux/netfilter/ip_set_chash.h>
-static inline void
-__nethash_list_header(const struct ip_set_nethash *map,
- struct ip_set_req_nethash_create *header)
-{
+static int
+hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+{
+ struct chash *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_net6_elem data = { .cidr = h->nets[0].cidr || HOST_MASK };
+
+ if (data.cidr == 0)
+ return -EINVAL;
+ if (adt == IPSET_TEST)
+ data.cidr = HOST_MASK;
+
+ ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+ ip6_netmask(&data.ip, data.cidr);
+
+ return adtfn(set, &data, GFP_ATOMIC, h->timeout);
+}
+
+static const struct nla_policy
+hash_net6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
+ [IPSET_ATTR_IP] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [IPSET_ATTR_CIDR] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static int
+hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
+{
+ struct chash *h = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_net6_elem data = { .cidr = HOST_MASK };
+ u32 timeout = h->timeout;
+
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ hash_net6_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_IP])
+ memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
+ sizeof(struct in6_addr));
+ else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_CIDR])
+ data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+
+ if (!data.cidr)
+ return -IPSET_ERR_INVALID_CIDR;
+
+ ip6_netmask(&data.ip, data.cidr);
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout(h->timeout))
+ return -IPSET_ERR_TIMEOUT;
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+ }
+
+ return adtfn(set, &data, GFP_KERNEL, timeout);
}
-HASH_LIST_HEADER(nethash)
-HASH_LIST_MEMBERS_SIZE(nethash, ip_set_ip_t)
-HASH_LIST_MEMBERS(nethash, ip_set_ip_t)
+/* Create hash:ip type of sets */
-IP_SET_RTYPE(nethash, IPSET_TYPE_IP | IPSET_DATA_SINGLE)
+static const struct nla_policy
+hash_net_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
+ [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
+ [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
+ [IPSET_ATTR_PROBES] = { .type = NLA_U8 },
+ [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-MODULE_DESCRIPTION("nethash type of IP sets");
-module_param(limit, int, 0600);
-MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets");
+static int
+hash_net_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
+{
+ struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+ struct chash *h;
+
+ if (!(set->family == AF_INET || set->family == AF_INET6))
+ return -IPSET_ERR_INVALID_FAMILY;
+
+ if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
+ hash_net_create_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_HASHSIZE]) {
+ hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
+ if (hashsize < IPSET_MIMINAL_HASHSIZE)
+ hashsize = IPSET_MIMINAL_HASHSIZE;
+ }
+
+ if (tb[IPSET_ATTR_MAXELEM])
+ maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
+
+ h = kzalloc(sizeof(*h)
+ + sizeof(struct chash_nets)
+ * (set->family == AF_INET ? 31 : 127), GFP_KERNEL);
+ if (!h)
+ return -ENOMEM;
+
+ h->maxelem = maxelem;
+ h->htable_bits = htable_bits(hashsize);
+ h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
+ h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
+ get_random_bytes(&h->initval, sizeof(h->initval));
+ h->timeout = IPSET_NO_TIMEOUT;
+
+ h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
+ GFP_KERNEL, &set->flags);
+ if (!h->htable) {
+ kfree(h);
+ return -ENOMEM;
+ }
+
+ set->data = h;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+
+ set->variant = set->family == AF_INET
+ ? &hash_net4_tvariant : &hash_net6_tvariant;
+
+ if (set->family == AF_INET)
+ hash_net4_gc_init(set);
+ else
+ hash_net6_gc_init(set);
+ } else {
+ set->variant = set->family == AF_INET
+ ? &hash_net4_variant : &hash_net6_variant;
+ }
+
+ pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)",
+ set->name, jhash_size(h->htable_bits),
+ h->htable_bits, h->maxelem, set->data, h->htable);
+
+ return 0;
+}
+
+static struct ip_set_type hash_net_type = {
+ .name = "hash:net",
+ .protocol = IPSET_PROTOCOL,
+ .features = IPSET_TYPE_IP,
+ .dimension = IPSET_DIM_ONE,
+ .family = AF_UNSPEC,
+ .revision = 0,
+ .create = hash_net_create,
+ .me = THIS_MODULE,
+};
+
+static int __init
+hash_net_init(void)
+{
+ return ip_set_type_register(&hash_net_type);
+}
+
+static void __exit
+hash_net_fini(void)
+{
+ ip_set_type_unregister(&hash_net_type);
+}
-REGISTER_MODULE(nethash)
+module_init(hash_net_init);
+module_exit(hash_net_fini);
diff --git a/kernel/ip_set_iptreemap.c b/kernel/ip_set_iptreemap.c
deleted file mode 100644
index 02f657e..0000000
--- a/kernel/ip_set_iptreemap.c
+++ /dev/null
@@ -1,700 +0,0 @@
-/* Copyright (C) 2007 Sven Wegener <sven.wegener@stealer.net>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-
-/* This modules implements the iptreemap ipset type. It uses bitmaps to
- * represent every single IPv4 address as a bit. The bitmaps are managed in a
- * tree structure, where the first three octets of an address are used as an
- * index to find the bitmap and the last octet is used as the bit number.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ip.h>
-#include <linux/jiffies.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <asm/uaccess.h>
-#include <asm/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/timer.h>
-
-#include <linux/netfilter_ipv4/ip_set.h>
-#include <linux/netfilter_ipv4/ip_set_bitmaps.h>
-#include <linux/netfilter_ipv4/ip_set_iptreemap.h>
-
-#define IPTREEMAP_DEFAULT_GC_TIME (5 * 60)
-#define IPTREEMAP_DESTROY_SLEEP (100)
-
-static __KMEM_CACHE_T__ *cachep_b;
-static __KMEM_CACHE_T__ *cachep_c;
-static __KMEM_CACHE_T__ *cachep_d;
-
-static struct ip_set_iptreemap_d *fullbitmap_d;
-static struct ip_set_iptreemap_c *fullbitmap_c;
-static struct ip_set_iptreemap_b *fullbitmap_b;
-
-#if defined(__LITTLE_ENDIAN)
-#define ABCD(a, b, c, d, addr) \
- do { \
- a = ((unsigned char *)addr)[3]; \
- b = ((unsigned char *)addr)[2]; \
- c = ((unsigned char *)addr)[1]; \
- d = ((unsigned char *)addr)[0]; \
- } while (0)
-#elif defined(__BIG_ENDIAN)
-#define ABCD(a,b,c,d,addrp) do { \
- a = ((unsigned char *)addrp)[0]; \
- b = ((unsigned char *)addrp)[1]; \
- c = ((unsigned char *)addrp)[2]; \
- d = ((unsigned char *)addrp)[3]; \
-} while (0)
-#else
-#error "Please fix asm/byteorder.h"
-#endif /* __LITTLE_ENDIAN */
-
-#define TESTIP_WALK(map, elem, branch, full) \
- do { \
- branch = (map)->tree[elem]; \
- if (!branch) \
- return 0; \
- else if (branch == full) \
- return 1; \
- } while (0)
-
-#define ADDIP_WALK(map, elem, branch, type, cachep, full) \
- do { \
- branch = (map)->tree[elem]; \
- if (!branch) { \
- branch = (type *) kmem_cache_alloc(cachep, GFP_ATOMIC); \
- if (!branch) \
- return -ENOMEM; \
- memset(branch, 0, sizeof(*branch)); \
- (map)->tree[elem] = branch; \
- } else if (branch == full) { \
- return -EEXIST; \
- } \
- } while (0)
-
-#define ADDIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free) \
- for (a = a1; a <= a2; a++) { \
- branch = (map)->tree[a]; \
- if (branch != full) { \
- if ((a > a1 && a < a2) || (hint)) { \
- if (branch) \
- free(branch); \
- (map)->tree[a] = full; \
- continue; \
- } else if (!branch) { \
- branch = kmem_cache_alloc(cachep, GFP_ATOMIC); \
- if (!branch) \
- return -ENOMEM; \
- memset(branch, 0, sizeof(*branch)); \
- (map)->tree[a] = branch; \
- }
-
-#define ADDIP_RANGE_LOOP_END() \
- } \
- }
-
-#define DELIP_WALK(map, elem, branch, cachep, full, flags) \
- do { \
- branch = (map)->tree[elem]; \
- if (!branch) { \
- return -EEXIST; \
- } else if (branch == full) { \
- branch = kmem_cache_alloc(cachep, flags); \
- if (!branch) \
- return -ENOMEM; \
- memcpy(branch, full, sizeof(*full)); \
- (map)->tree[elem] = branch; \
- } \
- } while (0)
-
-#define DELIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free, flags) \
- for (a = a1; a <= a2; a++) { \
- branch = (map)->tree[a]; \
- if (branch) { \
- if ((a > a1 && a < a2) || (hint)) { \
- if (branch != full) \
- free(branch); \
- (map)->tree[a] = NULL; \
- continue; \
- } else if (branch == full) { \
- branch = kmem_cache_alloc(cachep, flags); \
- if (!branch) \
- return -ENOMEM; \
- memcpy(branch, full, sizeof(*branch)); \
- (map)->tree[a] = branch; \
- }
-
-#define DELIP_RANGE_LOOP_END() \
- } \
- }
-
-#define LOOP_WALK_BEGIN(map, i, branch) \
- for (i = 0; i < 256; i++) { \
- branch = (map)->tree[i]; \
- if (likely(!branch)) \
- continue;
-
-#define LOOP_WALK_END() \
- }
-
-#define LOOP_WALK_BEGIN_GC(map, i, branch, full, cachep, count) \
- count = -256; \
- for (i = 0; i < 256; i++) { \
- branch = (map)->tree[i]; \
- if (likely(!branch)) \
- continue; \
- count++; \
- if (branch == full) { \
- count++; \
- continue; \
- }
-
-#define LOOP_WALK_END_GC(map, i, branch, full, cachep, count) \
- if (-256 == count) { \
- kmem_cache_free(cachep, branch); \
- (map)->tree[i] = NULL; \
- } else if (256 == count) { \
- kmem_cache_free(cachep, branch); \
- (map)->tree[i] = full; \
- } \
- }
-
-#define LOOP_WALK_BEGIN_COUNT(map, i, branch, inrange, count) \
- for (i = 0; i < 256; i++) { \
- if (!(map)->tree[i]) { \
- if (inrange) { \
- count++; \
- inrange = 0; \
- } \
- continue; \
- } \
- branch = (map)->tree[i];
-
-#define LOOP_WALK_END_COUNT() \
- }
-
-#define GETVALUE1(a, a1, b1, r) \
- (a == a1 ? b1 : r)
-
-#define GETVALUE2(a, b, a1, b1, c1, r) \
- (a == a1 && b == b1 ? c1 : r)
-
-#define GETVALUE3(a, b, c, a1, b1, c1, d1, r) \
- (a == a1 && b == b1 && c == c1 ? d1 : r)
-
-#define CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2) \
- ( \
- GETVALUE1(a, a1, b1, 0) == 0 \
- && GETVALUE1(a, a2, b2, 255) == 255 \
- && c1 == 0 \
- && c2 == 255 \
- && d1 == 0 \
- && d2 == 255 \
- )
-
-#define CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2) \
- ( \
- GETVALUE2(a, b, a1, b1, c1, 0) == 0 \
- && GETVALUE2(a, b, a2, b2, c2, 255) == 255 \
- && d1 == 0 \
- && d2 == 255 \
- )
-
-#define CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2) \
- ( \
- GETVALUE3(a, b, c, a1, b1, c1, d1, 0) == 0 \
- && GETVALUE3(a, b, c, a2, b2, c2, d2, 255) == 255 \
- )
-
-
-static inline void
-free_d(struct ip_set_iptreemap_d *map)
-{
- kmem_cache_free(cachep_d, map);
-}
-
-static inline void
-free_c(struct ip_set_iptreemap_c *map)
-{
- struct ip_set_iptreemap_d *dtree;
- unsigned int i;
-
- LOOP_WALK_BEGIN(map, i, dtree) {
- if (dtree != fullbitmap_d)
- free_d(dtree);
- } LOOP_WALK_END();
-
- kmem_cache_free(cachep_c, map);
-}
-
-static inline void
-free_b(struct ip_set_iptreemap_b *map)
-{
- struct ip_set_iptreemap_c *ctree;
- unsigned int i;
-
- LOOP_WALK_BEGIN(map, i, ctree) {
- if (ctree != fullbitmap_c)
- free_c(ctree);
- } LOOP_WALK_END();
-
- kmem_cache_free(cachep_b, map);
-}
-
-static inline int
-iptreemap_test(struct ip_set *set, ip_set_ip_t ip)
-{
- struct ip_set_iptreemap *map = set->data;
- struct ip_set_iptreemap_b *btree;
- struct ip_set_iptreemap_c *ctree;
- struct ip_set_iptreemap_d *dtree;
- unsigned char a, b, c, d;
-
- ABCD(a, b, c, d, &ip);
-
- TESTIP_WALK(map, a, btree, fullbitmap_b);
- TESTIP_WALK(btree, b, ctree, fullbitmap_c);
- TESTIP_WALK(ctree, c, dtree, fullbitmap_d);
-
- return !!test_bit(d, (void *) dtree->bitmap);
-}
-
-#define KADT_CONDITION
-
-UADT(iptreemap, test)
-KADT(iptreemap, test, ipaddr)
-
-static inline int
-__addip_single(struct ip_set *set, ip_set_ip_t ip)
-{
- struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data;
- struct ip_set_iptreemap_b *btree;
- struct ip_set_iptreemap_c *ctree;
- struct ip_set_iptreemap_d *dtree;
- unsigned char a, b, c, d;
-
- ABCD(a, b, c, d, &ip);
-
- ADDIP_WALK(map, a, btree, struct ip_set_iptreemap_b, cachep_b, fullbitmap_b);
- ADDIP_WALK(btree, b, ctree, struct ip_set_iptreemap_c, cachep_c, fullbitmap_c);
- ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreemap_d, cachep_d, fullbitmap_d);
-
- if (__test_and_set_bit(d, (void *) dtree->bitmap))
- return -EEXIST;
-
- __set_bit(b, (void *) btree->dirty);
-
- return 0;
-}
-
-static inline int
-iptreemap_add(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end)
-{
- struct ip_set_iptreemap *map = set->data;
- struct ip_set_iptreemap_b *btree;
- struct ip_set_iptreemap_c *ctree;
- struct ip_set_iptreemap_d *dtree;
- unsigned int a, b, c, d;
- unsigned char a1, b1, c1, d1;
- unsigned char a2, b2, c2, d2;
-
- if (start == end)
- return __addip_single(set, start);
-
- ABCD(a1, b1, c1, d1, &start);
- ABCD(a2, b2, c2, d2, &end);
-
- /* This is sooo ugly... */
- ADDIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b) {
- ADDIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c) {
- ADDIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d) {
- for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++)
- __set_bit(d, (void *) dtree->bitmap);
- __set_bit(b, (void *) btree->dirty);
- } ADDIP_RANGE_LOOP_END();
- } ADDIP_RANGE_LOOP_END();
- } ADDIP_RANGE_LOOP_END();
-
- return 0;
-}
-
-UADT0(iptreemap, add, min(req->ip, req->end), max(req->ip, req->end))
-KADT(iptreemap, add, ipaddr, ip)
-
-static inline int
-__delip_single(struct ip_set *set, ip_set_ip_t ip, gfp_t flags)
-{
- struct ip_set_iptreemap *map = set->data;
- struct ip_set_iptreemap_b *btree;
- struct ip_set_iptreemap_c *ctree;
- struct ip_set_iptreemap_d *dtree;
- unsigned char a,b,c,d;
-
- ABCD(a, b, c, d, &ip);
-
- DELIP_WALK(map, a, btree, cachep_b, fullbitmap_b, flags);
- DELIP_WALK(btree, b, ctree, cachep_c, fullbitmap_c, flags);
- DELIP_WALK(ctree, c, dtree, cachep_d, fullbitmap_d, flags);
-
- if (!__test_and_clear_bit(d, (void *) dtree->bitmap))
- return -EEXIST;
-
- __set_bit(b, (void *) btree->dirty);
-
- return 0;
-}
-
-static inline int
-iptreemap_del(struct ip_set *set,
- ip_set_ip_t start, ip_set_ip_t end, gfp_t flags)
-{
- struct ip_set_iptreemap *map = set->data;
- struct ip_set_iptreemap_b *btree;
- struct ip_set_iptreemap_c *ctree;
- struct ip_set_iptreemap_d *dtree;
- unsigned int a, b, c, d;
- unsigned char a1, b1, c1, d1;
- unsigned char a2, b2, c2, d2;
-
- if (start == end)
- return __delip_single(set, start, flags);
-
- ABCD(a1, b1, c1, d1, &start);
- ABCD(a2, b2, c2, d2, &end);
-
- /* This is sooo ugly... */
- DELIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b, flags) {
- DELIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c, flags) {
- DELIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d, flags) {
- for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++)
- __clear_bit(d, (void *) dtree->bitmap);
- __set_bit(b, (void *) btree->dirty);
- } DELIP_RANGE_LOOP_END();
- } DELIP_RANGE_LOOP_END();
- } DELIP_RANGE_LOOP_END();
-
- return 0;
-}
-
-UADT0(iptreemap, del, min(req->ip, req->end), max(req->ip, req->end), GFP_KERNEL)
-KADT(iptreemap, del, ipaddr, ip, GFP_ATOMIC)
-
-/* Check the status of the bitmap
- * -1 == all bits cleared
- * 1 == all bits set
- * 0 == anything else
- */
-static inline int
-bitmap_status(struct ip_set_iptreemap_d *dtree)
-{
- unsigned char first = dtree->bitmap[0];
- int a;
-
- for (a = 1; a < 32; a++)
- if (dtree->bitmap[a] != first)
- return 0;
-
- return (first == 0 ? -1 : (first == 255 ? 1 : 0));
-}
-
-static void
-gc(unsigned long addr)
-{
- struct ip_set *set = (struct ip_set *) addr;
- struct ip_set_iptreemap *map = set->data;
- struct ip_set_iptreemap_b *btree;
- struct ip_set_iptreemap_c *ctree;
- struct ip_set_iptreemap_d *dtree;
- unsigned int a, b, c;
- int i, j, k;
-
- write_lock_bh(&set->lock);
-
- LOOP_WALK_BEGIN_GC(map, a, btree, fullbitmap_b, cachep_b, i) {
- LOOP_WALK_BEGIN_GC(btree, b, ctree, fullbitmap_c, cachep_c, j) {
- if (!__test_and_clear_bit(b, (void *) btree->dirty))
- continue;
- LOOP_WALK_BEGIN_GC(ctree, c, dtree, fullbitmap_d, cachep_d, k) {
- switch (bitmap_status(dtree)) {
- case -1:
- kmem_cache_free(cachep_d, dtree);
- ctree->tree[c] = NULL;
- k--;
- break;
- case 1:
- kmem_cache_free(cachep_d, dtree);
- ctree->tree[c] = fullbitmap_d;
- k++;
- break;
- }
- } LOOP_WALK_END();
- } LOOP_WALK_END_GC(btree, b, ctree, fullbitmap_c, cachep_c, k);
- } LOOP_WALK_END_GC(map, a, btree, fullbitmap_b, cachep_b, j);
-
- write_unlock_bh(&set->lock);
-
- map->gc.expires = jiffies + map->gc_interval * HZ;
- add_timer(&map->gc);
-}
-
-static inline void
-init_gc_timer(struct ip_set *set)
-{
- struct ip_set_iptreemap *map = set->data;
-
- init_timer(&map->gc);
- map->gc.data = (unsigned long) set;
- map->gc.function = gc;
- map->gc.expires = jiffies + map->gc_interval * HZ;
- add_timer(&map->gc);
-}
-
-static int
-iptreemap_create(struct ip_set *set, const void *data, u_int32_t size)
-{
- const struct ip_set_req_iptreemap_create *req = data;
- struct ip_set_iptreemap *map;
-
- map = kzalloc(sizeof(*map), GFP_KERNEL);
- if (!map)
- return -ENOMEM;
-
- map->gc_interval = req->gc_interval ? req->gc_interval : IPTREEMAP_DEFAULT_GC_TIME;
- set->data = map;
-
- init_gc_timer(set);
-
- return 0;
-}
-
-static inline void
-__flush(struct ip_set_iptreemap *map)
-{
- struct ip_set_iptreemap_b *btree;
- unsigned int a;
-
- LOOP_WALK_BEGIN(map, a, btree);
- if (btree != fullbitmap_b)
- free_b(btree);
- LOOP_WALK_END();
-}
-
-static void
-iptreemap_destroy(struct ip_set *set)
-{
- struct ip_set_iptreemap *map = set->data;
-
- while (!del_timer(&map->gc))
- msleep(IPTREEMAP_DESTROY_SLEEP);
-
- __flush(map);
- kfree(map);
-
- set->data = NULL;
-}
-
-static void
-iptreemap_flush(struct ip_set *set)
-{
- struct ip_set_iptreemap *map = set->data;
- unsigned int gc_interval = map->gc_interval;
-
- while (!del_timer(&map->gc))
- msleep(IPTREEMAP_DESTROY_SLEEP);
-
- __flush(map);
-
- memset(map, 0, sizeof(*map));
- map->gc_interval = gc_interval;
-
- init_gc_timer(set);
-}
-
-static void
-iptreemap_list_header(const struct ip_set *set, void *data)
-{
- struct ip_set_iptreemap *map = set->data;
- struct ip_set_req_iptreemap_create *header = data;
-
- header->gc_interval = map->gc_interval;
-}
-
-static int
-iptreemap_list_members_size(const struct ip_set *set, char dont_align)
-{
- struct ip_set_iptreemap *map = set->data;
- struct ip_set_iptreemap_b *btree;
- struct ip_set_iptreemap_c *ctree;
- struct ip_set_iptreemap_d *dtree;
- unsigned int a, b, c, d, inrange = 0, count = 0;
-
- LOOP_WALK_BEGIN_COUNT(map, a, btree, inrange, count) {
- LOOP_WALK_BEGIN_COUNT(btree, b, ctree, inrange, count) {
- LOOP_WALK_BEGIN_COUNT(ctree, c, dtree, inrange, count) {
- for (d = 0; d < 256; d++) {
- if (test_bit(d, (void *) dtree->bitmap)) {
- inrange = 1;
- } else if (inrange) {
- count++;
- inrange = 0;
- }
- }
- } LOOP_WALK_END_COUNT();
- } LOOP_WALK_END_COUNT();
- } LOOP_WALK_END_COUNT();
-
- if (inrange)
- count++;
-
- return (count * IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align));
-}
-
-static inline void
-add_member(void *data, size_t offset, ip_set_ip_t start, ip_set_ip_t end)
-{
- struct ip_set_req_iptreemap *entry = data + offset;
-
- entry->ip = start;
- entry->end = end;
-}
-
-static void
-iptreemap_list_members(const struct ip_set *set, void *data, char dont_align)
-{
- struct ip_set_iptreemap *map = set->data;
- struct ip_set_iptreemap_b *btree;
- struct ip_set_iptreemap_c *ctree;
- struct ip_set_iptreemap_d *dtree;
- unsigned int a, b, c, d, inrange = 0;
- size_t offset = 0, datasize;
- ip_set_ip_t start = 0, end = 0, ip;
-
- datasize = IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align);
- LOOP_WALK_BEGIN(map, a, btree) {
- LOOP_WALK_BEGIN(btree, b, ctree) {
- LOOP_WALK_BEGIN(ctree, c, dtree) {
- for (d = 0; d < 256; d++) {
- if (test_bit(d, (void *) dtree->bitmap)) {
- ip = ((a << 24) | (b << 16) | (c << 8) | d);
- if (!inrange) {
- inrange = 1;
- start = ip;
- } else if (end < ip - 1) {
- add_member(data, offset, start, end);
- offset += datasize;
- start = ip;
- }
- end = ip;
- } else if (inrange) {
- add_member(data, offset, start, end);
- offset += datasize;
- inrange = 0;
- }
- }
- } LOOP_WALK_END();
- } LOOP_WALK_END();
- } LOOP_WALK_END();
-
- if (inrange)
- add_member(data, offset, start, end);
-}
-
-IP_SET_TYPE(iptreemap, IPSET_TYPE_IP | IPSET_DATA_SINGLE)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Sven Wegener <sven.wegener@stealer.net>");
-MODULE_DESCRIPTION("iptreemap type of IP sets");
-
-static int __init ip_set_iptreemap_init(void)
-{
- int ret = -ENOMEM;
- int a;
-
- cachep_b = KMEM_CACHE_CREATE("ip_set_iptreemap_b",
- sizeof(struct ip_set_iptreemap_b));
- if (!cachep_b) {
- ip_set_printk("Unable to create ip_set_iptreemap_b slab cache");
- goto out;
- }
-
- cachep_c = KMEM_CACHE_CREATE("ip_set_iptreemap_c",
- sizeof(struct ip_set_iptreemap_c));
- if (!cachep_c) {
- ip_set_printk("Unable to create ip_set_iptreemap_c slab cache");
- goto outb;
- }
-
- cachep_d = KMEM_CACHE_CREATE("ip_set_iptreemap_d",
- sizeof(struct ip_set_iptreemap_d));
- if (!cachep_d) {
- ip_set_printk("Unable to create ip_set_iptreemap_d slab cache");
- goto outc;
- }
-
- fullbitmap_d = kmem_cache_alloc(cachep_d, GFP_KERNEL);
- if (!fullbitmap_d)
- goto outd;
-
- fullbitmap_c = kmem_cache_alloc(cachep_c, GFP_KERNEL);
- if (!fullbitmap_c)
- goto outbitmapd;
-
- fullbitmap_b = kmem_cache_alloc(cachep_b, GFP_KERNEL);
- if (!fullbitmap_b)
- goto outbitmapc;
-
- ret = ip_set_register_set_type(&ip_set_iptreemap);
- if (0 > ret)
- goto outbitmapb;
-
- /* Now init our global bitmaps */
- memset(fullbitmap_d->bitmap, 0xff, sizeof(fullbitmap_d->bitmap));
-
- for (a = 0; a < 256; a++)
- fullbitmap_c->tree[a] = fullbitmap_d;
-
- for (a = 0; a < 256; a++)
- fullbitmap_b->tree[a] = fullbitmap_c;
- memset(fullbitmap_b->dirty, 0, sizeof(fullbitmap_b->dirty));
-
- return 0;
-
-outbitmapb:
- kmem_cache_free(cachep_b, fullbitmap_b);
-outbitmapc:
- kmem_cache_free(cachep_c, fullbitmap_c);
-outbitmapd:
- kmem_cache_free(cachep_d, fullbitmap_d);
-outd:
- kmem_cache_destroy(cachep_d);
-outc:
- kmem_cache_destroy(cachep_c);
-outb:
- kmem_cache_destroy(cachep_b);
-out:
-
- return ret;
-}
-
-static void __exit ip_set_iptreemap_fini(void)
-{
- ip_set_unregister_set_type(&ip_set_iptreemap);
- kmem_cache_free(cachep_d, fullbitmap_d);
- kmem_cache_free(cachep_c, fullbitmap_c);
- kmem_cache_free(cachep_b, fullbitmap_b);
- kmem_cache_destroy(cachep_d);
- kmem_cache_destroy(cachep_c);
- kmem_cache_destroy(cachep_b);
-}
-
-module_init(ip_set_iptreemap_init);
-module_exit(ip_set_iptreemap_fini);
diff --git a/kernel/ip_set_list_set.c b/kernel/ip_set_list_set.c
index 3cfdae8..ce6c4d1 100644
--- a/kernel/ip_set_list_set.c
+++ b/kernel/ip_set_list_set.c
@@ -1,324 +1,589 @@
-/* Copyright (C) 2008 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2008-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-/* Kernel module implementing an IP set type: the setlist type */
+/* Kernel module implementing an IP set type: the list:set type */
+#include <linux/netfilter/ip_set_kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
-#include <linux/netfilter_ipv4/ip_set.h>
-#include <linux/netfilter_ipv4/ip_set_bitmaps.h>
-#include <linux/netfilter_ipv4/ip_set_setlist.h>
+#include <linux/netfilter/ip_set.h>
+#include <linux/netfilter/ip_set_timeout.h>
+#include <linux/netfilter/ip_set_list.h>
-/*
- * before ==> index, ref
- * after ==> ref, index
- */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
+MODULE_DESCRIPTION("list:set type of IP sets");
+MODULE_ALIAS("ip_set_list:set");
+
+/* Member elements without and with timeout */
+struct set_elem {
+ ip_set_id_t id;
+};
+
+struct set_telem {
+ ip_set_id_t id;
+ unsigned long timeout;
+};
+
+/* Type structure */
+struct list_set {
+ size_t dsize; /* element size */
+ u32 size; /* size of set list array */
+ u32 timeout; /* timeout value */
+ struct timer_list gc; /* garbage collection */
+ struct set_elem members[0]; /* the set members */
+};
+
+static inline struct set_elem *
+list_set_elem(const struct list_set *map, u32 id)
+{
+ return (struct set_elem *)((char *)map->members + id * map->dsize);
+}
+
+static inline bool
+list_set_timeout(const struct list_set *map, u32 id)
+{
+ const struct set_telem *elem =
+ (const struct set_telem *) list_set_elem(map, id);
+
+ return ip_set_timeout_test(elem->timeout);
+}
+
+static inline bool
+list_set_expired(const struct list_set *map, u32 id)
+{
+ const struct set_telem *elem =
+ (const struct set_telem *) list_set_elem(map, id);
+
+ return ip_set_timeout_expired(elem->timeout);
+}
static inline int
-next_index_eq(const struct ip_set_setlist *map, int i, ip_set_id_t index)
+list_set_exist(const struct set_telem *elem)
{
- return i < map->size && map->index[i] == index;
+ return elem->id != IPSET_INVALID_ID
+ && !ip_set_timeout_expired(elem->timeout);
}
+/* Set list without and with timeout */
+
static int
-setlist_utest(struct ip_set *set, const void *data, u_int32_t size)
+list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
- const struct ip_set_setlist *map = set->data;
- const struct ip_set_req_setlist *req = data;
- ip_set_id_t index, ref = IP_SET_INVALID_ID;
- int i, res = 0;
- struct ip_set *s;
-
- if (req->before && req->ref[0] == '\0')
- return 0;
-
- index = __ip_set_get_byname(req->name, &s);
- if (index == IP_SET_INVALID_ID)
- return 0;
- if (req->ref[0] != '\0') {
- ref = __ip_set_get_byname(req->ref, &s);
- if (ref == IP_SET_INVALID_ID)
- goto finish;
- }
- for (i = 0; i < map->size
- && map->index[i] != IP_SET_INVALID_ID; i++) {
- if (req->before && map->index[i] == index) {
- res = next_index_eq(map, i + 1, ref);
- break;
- } else if (!req->before) {
- if ((ref == IP_SET_INVALID_ID
- && map->index[i] == index)
- || (map->index[i] == ref
- && next_index_eq(map, i + 1, index))) {
- res = 1;
- break;
- }
+ struct list_set *map = set->data;
+ struct set_elem *elem;
+ u32 i;
+ int ret;
+
+ for (i = 0; i < map->size; i++) {
+ elem = list_set_elem(map, i);
+ if (elem->id == IPSET_INVALID_ID)
+ return 0;
+ if (with_timeout(map->timeout) && list_set_expired(map, i))
+ continue;
+ switch (adt) {
+ case IPSET_TEST:
+ ret = ip_set_test(elem->id, skb, pf, dim, flags);
+ if (ret > 0)
+ return ret;
+ break;
+ case IPSET_ADD:
+ ret = ip_set_add(elem->id, skb, pf, dim, flags);
+ if (ret == 0)
+ return ret;
+ break;
+ case IPSET_DEL:
+ ret = ip_set_del(elem->id, skb, pf, dim, flags);
+ if (ret == 0)
+ return ret;
+ break;
+ default:
+ break;
}
}
- if (ref != IP_SET_INVALID_ID)
- __ip_set_put_byindex(ref);
-finish:
- __ip_set_put_byindex(index);
- return res;
+ return -EINVAL;
+}
+
+static const struct nla_policy
+list_set_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = {
+ [IPSET_ATTR_NAME] = { .type = NLA_STRING,
+ .len = IPSET_MAXNAMELEN },
+ [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
+ .len = IPSET_MAXNAMELEN },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+};
+
+static inline bool
+next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
+{
+ const struct set_elem *elem;
+
+ if (i + 1 < map->size) {
+ elem = list_set_elem(map, i + 1);
+ return !!(elem->id == id
+ && !(with_timeout(map->timeout)
+ && list_set_expired(map, i + 1)));
+ }
+
+ return 0;
}
+static inline void
+list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
+{
+ struct set_elem *e;
+
+ for (; i < map->size; i++) {
+ e = list_set_elem(map, i);
+ swap(e->id, id);
+ if (e->id == IPSET_INVALID_ID)
+ break;
+ }
+}
+
+static inline void
+list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
+ unsigned long timeout)
+{
+ struct set_telem *e;
+
+ for (; i < map->size; i++) {
+ e = (struct set_telem *)list_set_elem(map, i);
+ swap(e->id, id);
+ if (e->id == IPSET_INVALID_ID)
+ break;
+ swap(e->timeout, timeout);
+ }
+}
+
static int
-setlist_ktest(struct ip_set *set,
- const struct sk_buff *skb,
- const u_int32_t *flags)
+list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
+ unsigned long timeout)
{
- struct ip_set_setlist *map = set->data;
- int i, res = 0;
+ struct set_elem *e = list_set_elem(map, i);
+
+ if (i == map->size - 1 && e->id != IPSET_INVALID_ID)
+ /* Last element replaced: e.g. add new,before,last */
+ ip_set_put_byindex(e->id);
+ if (with_timeout(map->timeout))
+ list_elem_tadd(map, i, id, timeout);
+ else
+ list_elem_add(map, i, id);
- for (i = 0; i < map->size
- && map->index[i] != IP_SET_INVALID_ID
- && res == 0; i++)
- res = ip_set_testip_kernel(map->index[i], skb, flags);
- return res;
+ return 0;
}
-static inline int
-insert_setlist(struct ip_set_setlist *map, int i, ip_set_id_t index)
+static int
+list_set_del(struct list_set *map, ip_set_id_t id, u32 i)
{
- ip_set_id_t tmp;
- int j;
+ struct set_elem *a = list_set_elem(map, i), *b;
- DP("i: %u, last %u\n", i, map->index[map->size - 1]);
- if (i >= map->size || map->index[map->size - 1] != IP_SET_INVALID_ID)
- return -ERANGE;
-
- for (j = i; j < map->size
- && index != IP_SET_INVALID_ID; j++) {
- tmp = map->index[j];
- map->index[j] = index;
- index = tmp;
+ ip_set_put_byindex(id);
+
+ for (; i < map->size - 1; i++) {
+ b = list_set_elem(map, i + 1);
+ a->id = b->id;
+ if (with_timeout(map->timeout))
+ ((struct set_telem *)a)->timeout =
+ ((struct set_telem *)b)->timeout;
+ a = b;
+ if (a->id == IPSET_INVALID_ID)
+ break;
}
+ /* Last element */
+ a->id = IPSET_INVALID_ID;
return 0;
}
static int
-setlist_uadd(struct ip_set *set, const void *data, u_int32_t size)
+list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags)
{
- struct ip_set_setlist *map = set->data;
- const struct ip_set_req_setlist *req = data;
- ip_set_id_t index, ref = IP_SET_INVALID_ID;
- int i, res = -ERANGE;
+ struct list_set *map = set->data;
+ struct nlattr *tb[IPSET_ATTR_ADT_MAX];
+ bool eexist = flags & IPSET_FLAG_EXIST,
+ with_timeout = with_timeout(map->timeout);
+ int before = 0;
+ u32 timeout = map->timeout;
+ ip_set_id_t id, refid = IPSET_INVALID_ID;
+ struct set_elem *elem;
struct ip_set *s;
-
- if (req->before && req->ref[0] == '\0')
- return -EINVAL;
-
- index = __ip_set_get_byname(req->name, &s);
- if (index == IP_SET_INVALID_ID)
- return -EEXIST;
- /* "Loop detection" */
- if (strcmp(s->type->typename, "setlist") == 0)
+ u32 i;
+ int ret = 0;
+
+ if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
+ list_set_adt_policy))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_NAME]) {
+ id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
+ if (id == IPSET_INVALID_ID)
+ return -IPSET_ERR_NAME;
+ /* "Loop detection" */
+ if (s->type->features & IPSET_TYPE_NAME) {
+ ret = -IPSET_ERR_LOOP;
+ goto finish;
+ }
+ } else
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_CADT_FLAGS]) {
+ u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+ before = f & IPSET_FLAG_BEFORE;
+ }
+
+ if (before && !tb[IPSET_ATTR_NAMEREF]) {
+ ret = -IPSET_ERR_BEFORE;
goto finish;
+ }
- if (req->ref[0] != '\0') {
- ref = __ip_set_get_byname(req->ref, &s);
- if (ref == IP_SET_INVALID_ID) {
- res = -EEXIST;
+ if (tb[IPSET_ATTR_NAMEREF]) {
+ refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), &s);
+ if (refid == IPSET_INVALID_ID) {
+ ret = -IPSET_ERR_NAMEREF;
goto finish;
}
+ if (!before)
+ before = -1;
}
- for (i = 0; i < map->size; i++) {
- if (map->index[i] != ref)
- continue;
- if (req->before)
- res = insert_setlist(map, i, index);
- else
- res = insert_setlist(map,
- ref == IP_SET_INVALID_ID ? i : i + 1,
- index);
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!with_timeout) {
+ ret = -IPSET_ERR_TIMEOUT;
+ goto finish;
+ }
+ timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+ }
+
+ switch (adt) {
+ case IPSET_TEST:
+ for (i = 0; i < map->size && !ret; i++) {
+ elem = list_set_elem(map, i);
+ if (elem->id == IPSET_INVALID_ID
+ || (before != 0 && i + 1 >= map->size))
+ break;
+ else if (with_timeout && list_set_expired(map, i))
+ continue;
+ else if (before > 0 && elem->id == id)
+ ret = next_id_eq(map, i, refid);
+ else if (before < 0 && elem->id == refid)
+ ret = next_id_eq(map, i, id);
+ else if (before == 0 && elem->id == id)
+ ret = 1;
+ }
+ break;
+ case IPSET_ADD:
+ for (i = 0; i < map->size && !ret; i++) {
+ elem = list_set_elem(map, i);
+ if (elem->id == id
+ && !(with_timeout && list_set_expired(map, i)))
+ ret = -IPSET_ERR_EXIST;
+ }
+ if (ret == -IPSET_ERR_EXIST)
+ break;
+ ret = -IPSET_ERR_LIST_FULL;
+ for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
+ elem = list_set_elem(map, i);
+ if (elem->id == IPSET_INVALID_ID)
+ ret = before != 0 ? -IPSET_ERR_REF_EXIST
+ : list_set_add(map, i, id, timeout);
+ else if (elem->id != refid)
+ continue;
+ else if (with_timeout && list_set_expired(map, i))
+ ret = -IPSET_ERR_REF_EXIST;
+ else if (before)
+ ret = list_set_add(map, i, id, timeout);
+ else if (i + 1 < map->size)
+ ret = list_set_add(map, i + 1, id, timeout);
+ }
+ break;
+ case IPSET_DEL:
+ ret = -IPSET_ERR_EXIST;
+ for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
+ elem = list_set_elem(map, i);
+ if (elem->id == IPSET_INVALID_ID) {
+ ret = before != 0 ? -IPSET_ERR_REF_EXIST
+ : -IPSET_ERR_EXIST;
+ break;
+ } else if (with_timeout && list_set_expired(map, i))
+ continue;
+ else if (elem->id == id
+ && (before == 0
+ || (before > 0 && next_id_eq(map, i, refid))))
+ ret = list_set_del(map, id, i);
+ else if (before < 0 && elem->id == refid
+ && next_id_eq(map, i, id))
+ ret = list_set_del(map, id, i + 1);
+ }
+ break;
+ default:
break;
}
- if (ref != IP_SET_INVALID_ID)
- __ip_set_put_byindex(ref);
- /* In case of success, we keep the reference to the set */
+
finish:
- if (res != 0)
- __ip_set_put_byindex(index);
- return res;
+ if (refid != IPSET_INVALID_ID)
+ ip_set_put_byindex(refid);
+ if (adt != IPSET_ADD || ret)
+ ip_set_put_byindex(id);
+
+ if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+ return ret;
+ }
+ return ret;
}
-static int
-setlist_kadd(struct ip_set *set,
- const struct sk_buff *skb,
- const u_int32_t *flags)
+static void
+list_set_flush(struct ip_set *set)
{
- struct ip_set_setlist *map = set->data;
- int i, res = -EINVAL;
+ struct list_set *map = set->data;
+ struct set_elem *elem;
+ u32 i;
+
+ for (i = 0; i < map->size; i++) {
+ elem = list_set_elem(map, i);
+ if (elem->id != IPSET_INVALID_ID) {
+ ip_set_put_byindex(elem->id);
+ elem->id = IPSET_INVALID_ID;
+ }
+ }
+}
+
+static void
+list_set_destroy(struct ip_set *set)
+{
+ struct list_set *map = set->data;
+
+ if (with_timeout(map->timeout))
+ del_timer_sync(&map->gc);
+ list_set_flush(set);
+ kfree(map);
- for (i = 0; i < map->size
- && map->index[i] != IP_SET_INVALID_ID
- && res != 0; i++)
- res = ip_set_addip_kernel(map->index[i], skb, flags);
- return res;
+ set->data = NULL;
}
-static inline int
-unshift_setlist(struct ip_set_setlist *map, int i)
+static int
+list_set_head(struct ip_set *set, struct sk_buff *skb)
{
- int j;
+ const struct list_set *map = set->data;
+ struct nlattr *nested;
+
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested)
+ goto nla_put_failure;
+ NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size));
+ if (with_timeout(map->timeout))
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
+ NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
+ htonl(atomic_read(&set->ref) - 1));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
+ htonl(sizeof(*map) + map->size * map->dsize));
+ ipset_nest_end(skb, nested);
- for (j = i; j < map->size - 1; j++)
- map->index[j] = map->index[j+1];
- map->index[map->size-1] = IP_SET_INVALID_ID;
return 0;
+nla_put_failure:
+ return -EFAULT;
}
static int
-setlist_udel(struct ip_set *set, const void *data, u_int32_t size)
+list_set_list(struct ip_set *set,
+ struct sk_buff *skb, struct netlink_callback *cb)
{
- struct ip_set_setlist *map = set->data;
- const struct ip_set_req_setlist *req = data;
- ip_set_id_t index, ref = IP_SET_INVALID_ID;
- int i, res = -EEXIST;
- struct ip_set *s;
-
- if (req->before && req->ref[0] == '\0')
- return -EINVAL;
-
- index = __ip_set_get_byname(req->name, &s);
- if (index == IP_SET_INVALID_ID)
- return -EEXIST;
- if (req->ref[0] != '\0') {
- ref = __ip_set_get_byname(req->ref, &s);
- if (ref == IP_SET_INVALID_ID)
+ const struct list_set *map = set->data;
+ struct nlattr *atd, *nested;
+ u32 i, first = cb->args[2];
+ const struct set_elem *e;
+
+ atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
+ if (!atd)
+ return -EFAULT;
+ for (; cb->args[2] < map->size; cb->args[2]++) {
+ i = cb->args[2];
+ e = list_set_elem(map, i);
+ if (e->id == IPSET_INVALID_ID)
goto finish;
- }
- for (i = 0; i < map->size
- && map->index[i] != IP_SET_INVALID_ID; i++) {
- if (req->before) {
- if (map->index[i] == index
- && next_index_eq(map, i + 1, ref)) {
- res = unshift_setlist(map, i);
- break;
- }
- } else if (ref == IP_SET_INVALID_ID) {
- if (map->index[i] == index) {
- res = unshift_setlist(map, i);
- break;
- }
- } else if (map->index[i] == ref
- && next_index_eq(map, i + 1, index)) {
- res = unshift_setlist(map, i + 1);
- break;
+ if (with_timeout(map->timeout) && list_set_expired(map, i))
+ continue;
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested) {
+ if (i == first) {
+ nla_nest_cancel(skb, atd);
+ return -EFAULT;
+ } else
+ goto nla_put_failure;
}
+ NLA_PUT_STRING(skb, IPSET_ATTR_NAME,
+ ip_set_name_byindex(e->id));
+ if (with_timeout(map->timeout)) {
+ const struct set_telem *te =
+ (const struct set_telem *) e;
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
+ htonl(ip_set_timeout_get(te->timeout)));
+ }
+ ipset_nest_end(skb, nested);
}
- if (ref != IP_SET_INVALID_ID)
- __ip_set_put_byindex(ref);
finish:
- __ip_set_put_byindex(index);
- /* In case of success, release the reference to the set */
- if (res == 0)
- __ip_set_put_byindex(index);
- return res;
+ ipset_nest_end(skb, atd);
+ /* Set listing finished */
+ cb->args[2] = 0;
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nested);
+ ipset_nest_end(skb, atd);
+ return 0;
}
-static int
-setlist_kdel(struct ip_set *set,
- const struct sk_buff *skb,
- const u_int32_t *flags)
+static bool
+list_set_same_set(const struct ip_set *a, const struct ip_set *b)
{
- struct ip_set_setlist *map = set->data;
- int i, res = -EINVAL;
+ struct list_set *x = a->data;
+ struct list_set *y = b->data;
- for (i = 0; i < map->size
- && map->index[i] != IP_SET_INVALID_ID
- && res != 0; i++)
- res = ip_set_delip_kernel(map->index[i], skb, flags);
- return res;
+ return x->size == y->size
+ && x->timeout == y->timeout;
}
-static int
-setlist_create(struct ip_set *set, const void *data, u_int32_t size)
-{
- struct ip_set_setlist *map;
- const struct ip_set_req_setlist_create *req = data;
- int i;
-
- map = kmalloc(sizeof(struct ip_set_setlist) +
- req->size * sizeof(ip_set_id_t), GFP_KERNEL);
- if (!map)
- return -ENOMEM;
- map->size = req->size;
- for (i = 0; i < map->size; i++)
- map->index[i] = IP_SET_INVALID_ID;
-
- set->data = map;
- return 0;
-}
+static const struct ip_set_type_variant list_set __read_mostly = {
+ .kadt = list_set_kadt,
+ .uadt = list_set_uadt,
+ .destroy = list_set_destroy,
+ .flush = list_set_flush,
+ .head = list_set_head,
+ .list = list_set_list,
+ .same_set = list_set_same_set,
+};
static void
-setlist_destroy(struct ip_set *set)
+list_set_gc(unsigned long ul_set)
{
- struct ip_set_setlist *map = set->data;
- int i;
-
- for (i = 0; i < map->size
- && map->index[i] != IP_SET_INVALID_ID; i++)
- __ip_set_put_byindex(map->index[i]);
+ struct ip_set *set = (struct ip_set *) ul_set;
+ struct list_set *map = set->data;
+ struct set_telem *e;
+ u32 i;
- kfree(map);
- set->data = NULL;
+ /* We run parallel with other readers (test element)
+ * but adding/deleting new entries is locked out */
+ read_lock_bh(&set->lock);
+ for (i = map->size - 1; i >= 0; i--) {
+ e = (struct set_telem *) list_set_elem(map, i);
+ if (e->id != IPSET_INVALID_ID
+ && list_set_expired(map, i))
+ list_set_del(map, e->id, i);
+ }
+ read_unlock_bh(&set->lock);
+
+ map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+ add_timer(&map->gc);
}
-static void
-setlist_flush(struct ip_set *set)
+static inline void
+list_set_gc_init(struct ip_set *set)
{
- struct ip_set_setlist *map = set->data;
- int i;
-
- for (i = 0; i < map->size
- && map->index[i] != IP_SET_INVALID_ID; i++) {
- __ip_set_put_byindex(map->index[i]);
- map->index[i] = IP_SET_INVALID_ID;
- }
+ struct list_set *map = set->data;
+
+ init_timer(&map->gc);
+ map->gc.data = (unsigned long) set;
+ map->gc.function = list_set_gc;
+ map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+ add_timer(&map->gc);
}
-static void
-setlist_list_header(const struct ip_set *set, void *data)
+/* Create list:set type of sets */
+
+static const struct nla_policy
+list_set_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
+ [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static inline bool
+init_list_set(struct ip_set *set, u32 size, size_t dsize,
+ unsigned long timeout)
{
- const struct ip_set_setlist *map = set->data;
- struct ip_set_req_setlist_create *header = data;
+ struct list_set *map;
+ struct set_elem *e;
+ u32 i;
- header->size = map->size;
+ map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
+ if (!map)
+ return false;
+
+ map->size = size;
+ map->dsize = dsize;
+ map->timeout = timeout;
+ set->data = map;
+
+ for (i = 0; i < size; i++) {
+ e = list_set_elem(map, i);
+ e->id = IPSET_INVALID_ID;
+ }
+
+ return true;
}
static int
-setlist_list_members_size(const struct ip_set *set, char dont_align)
+list_set_create(struct ip_set *set, struct nlattr *head, int len,
+ u32 flags)
{
- const struct ip_set_setlist *map = set->data;
-
- return map->size * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align);
-}
+ struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
+ u32 size = IP_SET_LIST_DEFAULT_SIZE;
-static void
-setlist_list_members(const struct ip_set *set, void *data, char dont_align)
-{
- struct ip_set_setlist *map = set->data;
- ip_set_id_t *d;
- int i;
+ if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len,
+ list_set_create_policy))
+ return -IPSET_ERR_PROTOCOL;
- for (i = 0; i < map->size; i++) {
- d = data + i * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align);
- *d = ip_set_id(map->index[i]);
+ if (tb[IPSET_ATTR_SIZE])
+ size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
+ if (size < IP_SET_LIST_MIN_SIZE)
+ size = IP_SET_LIST_MIN_SIZE;
+
+ if (tb[IPSET_ATTR_TIMEOUT]) {
+ if (!init_list_set(set, size, sizeof(struct set_telem),
+ ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
+ return -ENOMEM;
+
+ list_set_gc_init(set);
+ } else {
+ if (!init_list_set(set, size, sizeof(struct set_elem),
+ IPSET_NO_TIMEOUT))
+ return -ENOMEM;
}
+ set->variant = &list_set;
+ return 0;
}
-IP_SET_TYPE(setlist, IPSET_TYPE_SETNAME | IPSET_DATA_SINGLE)
+static struct ip_set_type list_set_type = {
+ .name = "list:set",
+ .protocol = IPSET_PROTOCOL,
+ .features = IPSET_TYPE_NAME,
+ .dimension = IPSET_DIM_ONE,
+ .family = AF_UNSPEC,
+ .revision = 0,
+ .create = list_set_create,
+ .me = THIS_MODULE,
+};
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-MODULE_DESCRIPTION("setlist type of IP sets");
+static int __init
+list_set_init(void)
+{
+ return ip_set_type_register(&list_set_type);
+}
+
+static void __exit
+list_set_fini(void)
+{
+ ip_set_type_unregister(&list_set_type);
+}
-REGISTER_MODULE(setlist)
+module_init(list_set_init);
+module_exit(list_set_fini);
diff --git a/kernel/ip_set_tree_ip.c b/kernel/ip_set_tree_ip.c
deleted file mode 100644
index 77eb180..0000000
--- a/kernel/ip_set_tree_ip.c
+++ /dev/null
@@ -1,464 +0,0 @@
-/* Copyright (C) 2005-2008 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/* Kernel module implementing an IP set type: the iptree type */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/ip.h>
-#include <linux/jiffies.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <asm/uaccess.h>
-#include <asm/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/timer.h>
-
-#include <linux/netfilter_ipv4/ip_set.h>
-#include <linux/netfilter_ipv4/ip_set_bitmaps.h>
-#include <linux/netfilter_ipv4/ip_set_iptree.h>
-
-static int limit = MAX_RANGE;
-
-/* Garbage collection interval in seconds: */
-#define IPTREE_GC_TIME 5*60
-/* Sleep so many milliseconds before trying again
- * to delete the gc timer at destroying/flushing a set */
-#define IPTREE_DESTROY_SLEEP 100
-
-static __KMEM_CACHE_T__ *branch_cachep;
-static __KMEM_CACHE_T__ *leaf_cachep;
-
-
-#if defined(__LITTLE_ENDIAN)
-#define ABCD(a,b,c,d,addrp) do { \
- a = ((unsigned char *)addrp)[3]; \
- b = ((unsigned char *)addrp)[2]; \
- c = ((unsigned char *)addrp)[1]; \
- d = ((unsigned char *)addrp)[0]; \
-} while (0)
-#elif defined(__BIG_ENDIAN)
-#define ABCD(a,b,c,d,addrp) do { \
- a = ((unsigned char *)addrp)[0]; \
- b = ((unsigned char *)addrp)[1]; \
- c = ((unsigned char *)addrp)[2]; \
- d = ((unsigned char *)addrp)[3]; \
-} while (0)
-#else
-#error "Please fix asm/byteorder.h"
-#endif /* __LITTLE_ENDIAN */
-
-#define TESTIP_WALK(map, elem, branch) do { \
- if ((map)->tree[elem]) { \
- branch = (map)->tree[elem]; \
- } else \
- return 0; \
-} while (0)
-
-static inline int
-iptree_test(struct ip_set *set, ip_set_ip_t ip)
-{
- struct ip_set_iptree *map = set->data;
- struct ip_set_iptreeb *btree;
- struct ip_set_iptreec *ctree;
- struct ip_set_iptreed *dtree;
- unsigned char a,b,c,d;
-
- if (!ip)
- return -ERANGE;
-
- ABCD(a, b, c, d, &ip);
- DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout);
- TESTIP_WALK(map, a, btree);
- TESTIP_WALK(btree, b, ctree);
- TESTIP_WALK(ctree, c, dtree);
- DP("%lu %lu", dtree->expires[d], jiffies);
- return dtree->expires[d]
- && (!map->timeout
- || time_after(dtree->expires[d], jiffies));
-}
-
-#define KADT_CONDITION
-
-UADT(iptree, test)
-KADT(iptree, test, ipaddr)
-
-#define ADDIP_WALK(map, elem, branch, type, cachep) do { \
- if ((map)->tree[elem]) { \
- DP("found %u", elem); \
- branch = (map)->tree[elem]; \
- } else { \
- branch = (type *) \
- kmem_cache_alloc(cachep, GFP_ATOMIC); \
- if (branch == NULL) \
- return -ENOMEM; \
- memset(branch, 0, sizeof(*branch)); \
- (map)->tree[elem] = branch; \
- DP("alloc %u", elem); \
- } \
-} while (0)
-
-static inline int
-iptree_add(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout)
-{
- struct ip_set_iptree *map = set->data;
- struct ip_set_iptreeb *btree;
- struct ip_set_iptreec *ctree;
- struct ip_set_iptreed *dtree;
- unsigned char a,b,c,d;
- int ret = 0;
-
- if (!ip || map->elements >= limit)
- /* We could call the garbage collector
- * but it's probably overkill */
- return -ERANGE;
-
- ABCD(a, b, c, d, &ip);
- DP("%u %u %u %u timeout %u", a, b, c, d, timeout);
- ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep);
- ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep);
- ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreed, leaf_cachep);
- if (dtree->expires[d]
- && (!map->timeout || time_after(dtree->expires[d], jiffies)))
- ret = -EEXIST;
- if (map->timeout && timeout == 0)
- timeout = map->timeout;
- dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1;
- /* Lottery: I won! */
- if (dtree->expires[d] == 0)
- dtree->expires[d] = 1;
- DP("%u %lu", d, dtree->expires[d]);
- if (ret == 0)
- map->elements++;
- return ret;
-}
-
-UADT(iptree, add, req->timeout)
-KADT(iptree, add, ipaddr, 0)
-
-#define DELIP_WALK(map, elem, branch) do { \
- if ((map)->tree[elem]) { \
- branch = (map)->tree[elem]; \
- } else \
- return -EEXIST; \
-} while (0)
-
-static inline int
-iptree_del(struct ip_set *set, ip_set_ip_t ip)
-{
- struct ip_set_iptree *map = set->data;
- struct ip_set_iptreeb *btree;
- struct ip_set_iptreec *ctree;
- struct ip_set_iptreed *dtree;
- unsigned char a,b,c,d;
-
- if (!ip)
- return -ERANGE;
-
- ABCD(a, b, c, d, &ip);
- DELIP_WALK(map, a, btree);
- DELIP_WALK(btree, b, ctree);
- DELIP_WALK(ctree, c, dtree);
-
- if (dtree->expires[d]) {
- dtree->expires[d] = 0;
- map->elements--;
- return 0;
- }
- return -EEXIST;
-}
-
-UADT(iptree, del)
-KADT(iptree, del, ipaddr)
-
-#define LOOP_WALK_BEGIN(map, i, branch) \
- for (i = 0; i < 256; i++) { \
- if (!(map)->tree[i]) \
- continue; \
- branch = (map)->tree[i]
-
-#define LOOP_WALK_END }
-
-static void
-ip_tree_gc(unsigned long ul_set)
-{
- struct ip_set *set = (struct ip_set *) ul_set;
- struct ip_set_iptree *map = set->data;
- struct ip_set_iptreeb *btree;
- struct ip_set_iptreec *ctree;
- struct ip_set_iptreed *dtree;
- unsigned int a,b,c,d;
- unsigned char i,j,k;
-
- i = j = k = 0;
- DP("gc: %s", set->name);
- write_lock_bh(&set->lock);
- LOOP_WALK_BEGIN(map, a, btree);
- LOOP_WALK_BEGIN(btree, b, ctree);
- LOOP_WALK_BEGIN(ctree, c, dtree);
- for (d = 0; d < 256; d++) {
- if (dtree->expires[d]) {
- DP("gc: %u %u %u %u: expires %lu jiffies %lu",
- a, b, c, d,
- dtree->expires[d], jiffies);
- if (map->timeout
- && time_before(dtree->expires[d], jiffies)) {
- dtree->expires[d] = 0;
- map->elements--;
- } else
- k = 1;
- }
- }
- if (k == 0) {
- DP("gc: %s: leaf %u %u %u empty",
- set->name, a, b, c);
- kmem_cache_free(leaf_cachep, dtree);
- ctree->tree[c] = NULL;
- } else {
- DP("gc: %s: leaf %u %u %u not empty",
- set->name, a, b, c);
- j = 1;
- k = 0;
- }
- LOOP_WALK_END;
- if (j == 0) {
- DP("gc: %s: branch %u %u empty",
- set->name, a, b);
- kmem_cache_free(branch_cachep, ctree);
- btree->tree[b] = NULL;
- } else {
- DP("gc: %s: branch %u %u not empty",
- set->name, a, b);
- i = 1;
- j = k = 0;
- }
- LOOP_WALK_END;
- if (i == 0) {
- DP("gc: %s: branch %u empty",
- set->name, a);
- kmem_cache_free(branch_cachep, btree);
- map->tree[a] = NULL;
- } else {
- DP("gc: %s: branch %u not empty",
- set->name, a);
- i = j = k = 0;
- }
- LOOP_WALK_END;
- write_unlock_bh(&set->lock);
-
- map->gc.expires = jiffies + map->gc_interval * HZ;
- add_timer(&map->gc);
-}
-
-static inline void
-init_gc_timer(struct ip_set *set)
-{
- struct ip_set_iptree *map = set->data;
-
- /* Even if there is no timeout for the entries,
- * we still have to call gc because delete
- * do not clean up empty branches */
- map->gc_interval = IPTREE_GC_TIME;
- init_timer(&map->gc);
- map->gc.data = (unsigned long) set;
- map->gc.function = ip_tree_gc;
- map->gc.expires = jiffies + map->gc_interval * HZ;
- add_timer(&map->gc);
-}
-
-static int
-iptree_create(struct ip_set *set, const void *data, u_int32_t size)
-{
- const struct ip_set_req_iptree_create *req = data;
- struct ip_set_iptree *map;
-
- if (size != sizeof(struct ip_set_req_iptree_create)) {
- ip_set_printk("data length wrong (want %zu, have %lu)",
- sizeof(struct ip_set_req_iptree_create),
- (unsigned long)size);
- return -EINVAL;
- }
-
- map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL);
- if (!map) {
- DP("out of memory for %zu bytes",
- sizeof(struct ip_set_iptree));
- return -ENOMEM;
- }
- memset(map, 0, sizeof(*map));
- map->timeout = req->timeout;
- map->elements = 0;
- set->data = map;
-
- init_gc_timer(set);
-
- return 0;
-}
-
-static inline void
-__flush(struct ip_set_iptree *map)
-{
- struct ip_set_iptreeb *btree;
- struct ip_set_iptreec *ctree;
- struct ip_set_iptreed *dtree;
- unsigned int a,b,c;
-
- LOOP_WALK_BEGIN(map, a, btree);
- LOOP_WALK_BEGIN(btree, b, ctree);
- LOOP_WALK_BEGIN(ctree, c, dtree);
- kmem_cache_free(leaf_cachep, dtree);
- LOOP_WALK_END;
- kmem_cache_free(branch_cachep, ctree);
- LOOP_WALK_END;
- kmem_cache_free(branch_cachep, btree);
- LOOP_WALK_END;
- map->elements = 0;
-}
-
-static void
-iptree_destroy(struct ip_set *set)
-{
- struct ip_set_iptree *map = set->data;
-
- /* gc might be running */
- while (!del_timer(&map->gc))
- msleep(IPTREE_DESTROY_SLEEP);
- __flush(map);
- kfree(map);
- set->data = NULL;
-}
-
-static void
-iptree_flush(struct ip_set *set)
-{
- struct ip_set_iptree *map = set->data;
- unsigned int timeout = map->timeout;
-
- /* gc might be running */
- while (!del_timer(&map->gc))
- msleep(IPTREE_DESTROY_SLEEP);
- __flush(map);
- memset(map, 0, sizeof(*map));
- map->timeout = timeout;
-
- init_gc_timer(set);
-}
-
-static void
-iptree_list_header(const struct ip_set *set, void *data)
-{
- const struct ip_set_iptree *map = set->data;
- struct ip_set_req_iptree_create *header = data;
-
- header->timeout = map->timeout;
-}
-
-static int
-iptree_list_members_size(const struct ip_set *set, char dont_align)
-{
- const struct ip_set_iptree *map = set->data;
- struct ip_set_iptreeb *btree;
- struct ip_set_iptreec *ctree;
- struct ip_set_iptreed *dtree;
- unsigned int a,b,c,d;
- unsigned int count = 0;
-
- LOOP_WALK_BEGIN(map, a, btree);
- LOOP_WALK_BEGIN(btree, b, ctree);
- LOOP_WALK_BEGIN(ctree, c, dtree);
- for (d = 0; d < 256; d++) {
- if (dtree->expires[d]
- && (!map->timeout || time_after(dtree->expires[d], jiffies)))
- count++;
- }
- LOOP_WALK_END;
- LOOP_WALK_END;
- LOOP_WALK_END;
-
- DP("members %u", count);
- return (count * IPSET_VALIGN(sizeof(struct ip_set_req_iptree), dont_align));
-}
-
-static void
-iptree_list_members(const struct ip_set *set, void *data, char dont_align)
-{
- const struct ip_set_iptree *map = set->data;
- struct ip_set_iptreeb *btree;
- struct ip_set_iptreec *ctree;
- struct ip_set_iptreed *dtree;
- unsigned int a,b,c,d;
- size_t offset = 0, datasize;
- struct ip_set_req_iptree *entry;
-
- datasize = IPSET_VALIGN(sizeof(struct ip_set_req_iptree), dont_align);
- LOOP_WALK_BEGIN(map, a, btree);
- LOOP_WALK_BEGIN(btree, b, ctree);
- LOOP_WALK_BEGIN(ctree, c, dtree);
- for (d = 0; d < 256; d++) {
- if (dtree->expires[d]
- && (!map->timeout || time_after(dtree->expires[d], jiffies))) {
- entry = data + offset;
- entry->ip = ((a << 24) | (b << 16) | (c << 8) | d);
- entry->timeout = !map->timeout ? 0
- : (dtree->expires[d] - jiffies)/HZ;
- offset += datasize;
- }
- }
- LOOP_WALK_END;
- LOOP_WALK_END;
- LOOP_WALK_END;
-}
-
-IP_SET_TYPE(iptree, IPSET_TYPE_IP | IPSET_DATA_SINGLE)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-MODULE_DESCRIPTION("iptree type of IP sets");
-module_param(limit, int, 0600);
-MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets");
-
-static int __init ip_set_iptree_init(void)
-{
- int ret;
-
- branch_cachep = KMEM_CACHE_CREATE("ip_set_iptreeb",
- sizeof(struct ip_set_iptreeb));
- if (!branch_cachep) {
- printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n");
- ret = -ENOMEM;
- goto out;
- }
- leaf_cachep = KMEM_CACHE_CREATE("ip_set_iptreed",
- sizeof(struct ip_set_iptreed));
- if (!leaf_cachep) {
- printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n");
- ret = -ENOMEM;
- goto free_branch;
- }
- ret = ip_set_register_set_type(&ip_set_iptree);
- if (ret == 0)
- goto out;
-
- kmem_cache_destroy(leaf_cachep);
- free_branch:
- kmem_cache_destroy(branch_cachep);
- out:
- return ret;
-}
-
-static void __exit ip_set_iptree_fini(void)
-{
- /* FIXME: possible race with ip_set_create() */
- ip_set_unregister_set_type(&ip_set_iptree);
- kmem_cache_destroy(leaf_cachep);
- kmem_cache_destroy(branch_cachep);
-}
-
-module_init(ip_set_iptree_init);
-module_exit(ip_set_iptree_fini);
diff --git a/kernel/ipt_SET.c b/kernel/ipt_SET.c
deleted file mode 100644
index 6009d64..0000000
--- a/kernel/ipt_SET.c
+++ /dev/null
@@ -1,242 +0,0 @@
-/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
- * Patrick Schaaf <bof@bof.de>
- * Martin Josefsson <gandalf@wlug.westbo.se>
- * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/* ipt_SET.c - netfilter target to manipulate IP sets */
-
-#include <linux/module.h>
-#include <linux/ip.h>
-#include <linux/skbuff.h>
-#include <linux/version.h>
-
-#include <linux/netfilter_ipv4.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-#include <linux/netfilter_ipv4/ip_tables.h>
-#define xt_register_target ipt_register_target
-#define xt_unregister_target ipt_unregister_target
-#define xt_target ipt_target
-#define XT_CONTINUE IPT_CONTINUE
-#else
-#include <linux/netfilter/x_tables.h>
-#endif
-#include <linux/netfilter_ipv4/ipt_set.h>
-
-static unsigned int
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-target(struct sk_buff **pskb,
- unsigned int hooknum,
- const struct net_device *in,
- const struct net_device *out,
- const void *targinfo,
- void *userinfo)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-target(struct sk_buff **pskb,
- const struct net_device *in,
- const struct net_device *out,
- unsigned int hooknum,
- const void *targinfo,
- void *userinfo)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-target(struct sk_buff **pskb,
- const struct net_device *in,
- const struct net_device *out,
- unsigned int hooknum,
- const struct xt_target *target,
- const void *targinfo,
- void *userinfo)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
-target(struct sk_buff **pskb,
- const struct net_device *in,
- const struct net_device *out,
- unsigned int hooknum,
- const struct xt_target *target,
- const void *targinfo)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-target(struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- unsigned int hooknum,
- const struct xt_target *target,
- const void *targinfo)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
-target(struct sk_buff *skb,
- const struct xt_target_param *par)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- const struct ipt_set_info_target *info = targinfo;
-#else
- const struct ipt_set_info_target *info = par->targinfo;
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
- struct sk_buff *skb = *pskb;
-#endif
-
-
- if (info->add_set.index != IP_SET_INVALID_ID)
- ip_set_addip_kernel(info->add_set.index,
- skb,
- info->add_set.flags);
- if (info->del_set.index != IP_SET_INVALID_ID)
- ip_set_delip_kernel(info->del_set.index,
- skb,
- info->del_set.flags);
-
- return XT_CONTINUE;
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-static int
-checkentry(const char *tablename,
- const struct ipt_entry *e,
- void *targinfo,
- unsigned int targinfosize,
- unsigned int hook_mask)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-static int
-checkentry(const char *tablename,
- const void *e,
- void *targinfo,
- unsigned int targinfosize,
- unsigned int hook_mask)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-static int
-checkentry(const char *tablename,
- const void *e,
- const struct xt_target *target,
- void *targinfo,
- unsigned int targinfosize,
- unsigned int hook_mask)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-static int
-checkentry(const char *tablename,
- const void *e,
- const struct xt_target *target,
- void *targinfo,
- unsigned int hook_mask)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-static bool
-checkentry(const char *tablename,
- const void *e,
- const struct xt_target *target,
- void *targinfo,
- unsigned int hook_mask)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
-static bool
-checkentry(const struct xt_tgchk_param *par)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- const struct ipt_set_info_target *info = targinfo;
-#else
- const struct ipt_set_info_target *info = par->targinfo;
-#endif
- ip_set_id_t index;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
- if (targinfosize != IPT_ALIGN(sizeof(*info))) {
- DP("bad target info size %u", targinfosize);
- return 0;
- }
-#endif
-
- 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 */
- }
- }
- if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0
- || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) {
- ip_set_printk("That's nasty!");
- return 0; /* error */
- }
-
- return 1;
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-static void destroy(void *targetinfo,
- unsigned int targetsize)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-static void destroy(const struct xt_target *target,
- void *targetinfo,
- unsigned int targetsize)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-static void destroy(const struct xt_target *target,
- void *targetinfo)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
-static void destroy(const struct xt_tgdtor_param *par)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- const struct ipt_set_info_target *info = targetinfo;
-#else
- const struct ipt_set_info_target *info = par->targinfo;
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
- if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) {
- ip_set_printk("invalid targetsize %d", targetsize);
- return;
- }
-#endif
- if (info->add_set.index != IP_SET_INVALID_ID)
- ip_set_put_byindex(info->add_set.index);
- if (info->del_set.index != IP_SET_INVALID_ID)
- ip_set_put_byindex(info->del_set.index);
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-static struct xt_target SET_target = {
- .name = "SET",
- .target = target,
- .checkentry = checkentry,
- .destroy = destroy,
- .me = THIS_MODULE
-};
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */
-static struct xt_target SET_target = {
- .name = "SET",
- .family = AF_INET,
- .target = target,
- .targetsize = sizeof(struct ipt_set_info_target),
- .checkentry = checkentry,
- .destroy = destroy,
- .me = THIS_MODULE
-};
-#endif
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-MODULE_DESCRIPTION("iptables IP set target module");
-
-static int __init ipt_SET_init(void)
-{
- return xt_register_target(&SET_target);
-}
-
-static void __exit ipt_SET_fini(void)
-{
- xt_unregister_target(&SET_target);
-}
-
-module_init(ipt_SET_init);
-module_exit(ipt_SET_fini);
diff --git a/kernel/ipt_set.c b/kernel/ipt_set.c
deleted file mode 100644
index 2f97cbb..0000000
--- a/kernel/ipt_set.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
- * Patrick Schaaf <bof@bof.de>
- * Martin Josefsson <gandalf@wlug.westbo.se>
- * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/* Kernel module to match an IP set. */
-
-#include <linux/module.h>
-#include <linux/ip.h>
-#include <linux/skbuff.h>
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-#include <linux/netfilter_ipv4/ip_tables.h>
-#define xt_register_match ipt_register_match
-#define xt_unregister_match ipt_unregister_match
-#define xt_match ipt_match
-#else
-#include <linux/netfilter/x_tables.h>
-#endif
-#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;
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-static int
-match(const struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- const void *matchinfo,
- int offset,
- const void *hdr,
- u_int16_t datalen,
- int *hotdrop)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-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)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-static int
-match(const struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- const void *matchinfo,
- int offset,
- unsigned int protoff,
- int *hotdrop)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-static int
-match(const struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- const struct xt_match *match,
- const void *matchinfo,
- int offset,
- unsigned int protoff,
- int *hotdrop)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-static bool
-match(const struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- const struct xt_match *match,
- const void *matchinfo,
- int offset,
- unsigned int protoff,
- bool *hotdrop)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
-static bool
-match(const struct sk_buff *skb,
- const struct xt_match_param *par)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- const struct ipt_set_info_match *info = matchinfo;
-#else
- const struct ipt_set_info_match *info = par->matchinfo;
-#endif
-
- return match_set(&info->match_set,
- skb,
- info->match_set.flags[0] & IPSET_MATCH_INV);
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-static int
-checkentry(const char *tablename,
- const struct ipt_ip *ip,
- void *matchinfo,
- unsigned int matchsize,
- unsigned int hook_mask)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-static int
-checkentry(const char *tablename,
- const void *inf,
- void *matchinfo,
- unsigned int matchsize,
- unsigned int hook_mask)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-static int
-checkentry(const char *tablename,
- const void *inf,
- const struct xt_match *match,
- void *matchinfo,
- unsigned int matchsize,
- unsigned int hook_mask)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-static int
-checkentry(const char *tablename,
- const void *inf,
- const struct xt_match *match,
- void *matchinfo,
- unsigned int hook_mask)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-static bool
-checkentry(const char *tablename,
- const void *inf,
- const struct xt_match *match,
- void *matchinfo,
- unsigned int hook_mask)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
-static bool
-checkentry(const struct xt_mtchk_param *par)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- struct ipt_set_info_match *info = matchinfo;
-#else
- struct ipt_set_info_match *info = par->matchinfo;
-#endif
- ip_set_id_t index;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
- if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) {
- ip_set_printk("invalid matchsize %d", matchsize);
- return 0;
- }
-#endif
-
- 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 */
- }
- if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) {
- ip_set_printk("That's nasty!");
- return 0; /* error */
- }
-
- return 1;
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-static void destroy(void *matchinfo,
- unsigned int matchsize)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-static void destroy(const struct xt_match *match,
- void *matchinfo,
- unsigned int matchsize)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-static void destroy(const struct xt_match *match,
- void *matchinfo)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
-static void destroy(const struct xt_mtdtor_param *par)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- struct ipt_set_info_match *info = matchinfo;
-#else
- struct ipt_set_info_match *info = par->matchinfo;
-#endif
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
- if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) {
- ip_set_printk("invalid matchsize %d", matchsize);
- return;
- }
-#endif
- ip_set_put_byindex(info->match_set.index);
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-static struct xt_match set_match = {
- .name = "set",
- .match = &match,
- .checkentry = &checkentry,
- .destroy = &destroy,
- .me = THIS_MODULE
-};
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */
-static struct xt_match set_match = {
- .name = "set",
- .family = AF_INET,
- .match = &match,
- .matchsize = sizeof(struct ipt_set_info_match),
- .checkentry = &checkentry,
- .destroy = &destroy,
- .me = THIS_MODULE
-};
-#endif
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-MODULE_DESCRIPTION("iptables IP set match module");
-
-static int __init ipt_ipset_init(void)
-{
- return xt_register_match(&set_match);
-}
-
-static void __exit ipt_ipset_fini(void)
-{
- xt_unregister_match(&set_match);
-}
-
-module_init(ipt_ipset_init);
-module_exit(ipt_ipset_fini);
diff --git a/kernel/xt_set.c b/kernel/xt_set.c
new file mode 100644
index 0000000..7dfd5ee
--- /dev/null
+++ b/kernel/xt_set.c
@@ -0,0 +1,356 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ * Patrick Schaaf <bof@bof.de>
+ * Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Kernel module which implements the set match and SET target
+ * for netfilter/iptables. */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_set.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
+MODULE_DESCRIPTION("Xtables: IP set match and target module");
+MODULE_ALIAS("xt_SET");
+MODULE_ALIAS("ipt_set");
+MODULE_ALIAS("ipt_SET");
+
+static inline int
+match_set(ip_set_id_t index, const struct sk_buff *skb,
+ u8 pf, u8 dim, u8 flags, int inv)
+{
+ if (ip_set_test(index, skb, pf, dim, flags))
+ inv = !inv;
+ return inv;
+}
+
+/* Revision 0 interface: backward compatible with netfilter/iptables */
+
+static bool
+set_match_v0(const struct sk_buff *skb, const struct xt_match_param *par)
+{
+ const struct xt_set_info_match_v0 *info = par->matchinfo;
+
+ return match_set(info->match_set.index, skb, par->family,
+ info->match_set.u.compat.dim,
+ info->match_set.u.compat.flags,
+ info->match_set.u.compat.flags & IPSET_INV_MATCH);
+}
+
+static void
+compat_flags(struct xt_set_info_v0 *info)
+{
+ u_int8_t i;
+
+ /* Fill out compatibility data according to enum ip_set_kopt */
+ info->u.compat.dim = IPSET_DIM_ZERO;
+ if (info->u.flags[0] & IPSET_MATCH_INV)
+ info->u.compat.flags |= IPSET_INV_MATCH;
+ for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
+ info->u.compat.dim++;
+ if (info->u.flags[i] & IPSET_SRC)
+ info->u.compat.flags |= (1<<info->u.compat.dim);
+ }
+}
+
+static bool
+set_match_v0_checkentry(const struct xt_mtchk_param *par)
+{
+ struct xt_set_info_match_v0 *info = par->matchinfo;
+ ip_set_id_t index;
+
+ index = ip_set_nfnl_get_byindex(info->match_set.index);
+
+ if (index == IPSET_INVALID_ID) {
+ pr_warning("Cannot find set indentified by id %u to match",
+ info->match_set.index);
+ return 0; /* error */
+ }
+ if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
+ pr_warning("That's nasty!");
+ return 0; /* error */
+ }
+
+ /* Fill out compatibility data */
+ compat_flags(&info->match_set);
+
+ return 1;
+}
+
+static void
+set_match_v0_destroy(const struct xt_mtdtor_param *par)
+{
+ struct xt_set_info_match *info = par->matchinfo;
+
+
+ ip_set_nfnl_put(info->match_set.index);
+}
+
+static unsigned int
+set_target_v0(struct sk_buff *skb, const struct xt_target_param *par)
+{
+ const struct xt_set_info_target_v0 *info = par->targinfo;
+
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_add(info->add_set.index, skb, par->family,
+ info->add_set.u.compat.dim,
+ info->add_set.u.compat.flags);
+ if (info->del_set.index != IPSET_INVALID_ID)
+ ip_set_del(info->del_set.index, skb, par->family,
+ info->del_set.u.compat.dim,
+ info->del_set.u.compat.flags);
+
+ return XT_CONTINUE;
+}
+
+static bool
+set_target_v0_checkentry(const struct xt_tgchk_param *par)
+{
+ struct xt_set_info_target_v0 *info = par->targinfo;
+ ip_set_id_t index;
+
+ if (info->add_set.index != IPSET_INVALID_ID) {
+ index = ip_set_nfnl_get_byindex(info->add_set.index);
+ if (index == IPSET_INVALID_ID) {
+ pr_warning("cannot find add_set index %u as target",
+ info->add_set.index);
+ return 0; /* error */
+ }
+ }
+
+ if (info->del_set.index != IPSET_INVALID_ID) {
+ index = ip_set_nfnl_get_byindex(info->del_set.index);
+ if (index == IPSET_INVALID_ID) {
+ pr_warning("cannot find del_set index %u as target",
+ info->del_set.index);
+ return 0; /* error */
+ }
+ }
+ if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0
+ || info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
+ pr_warning("That's nasty!");
+ return 0; /* error */
+ }
+
+ /* Fill out compatibility data */
+ compat_flags(&info->add_set);
+ compat_flags(&info->del_set);
+
+ return 1;
+}
+
+static void
+set_target_v0_destroy(const struct xt_tgdtor_param *par)
+{
+ const struct xt_set_info_target_v0 *info = par->targinfo;
+
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->add_set.index);
+ if (info->del_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->del_set.index);
+}
+
+/* Revision 1: current interface to netfilter/iptables */
+
+static bool
+set_match(const struct sk_buff *skb, const struct xt_match_param *par)
+{
+ const struct xt_set_info_match *info = par->matchinfo;
+
+ return match_set(info->match_set.index, skb, par->family,
+ info->match_set.dim,
+ info->match_set.flags,
+ info->match_set.flags & IPSET_INV_MATCH);
+}
+
+static bool
+set_match_checkentry(const struct xt_mtchk_param *par)
+{
+ struct xt_set_info_match *info = par->matchinfo;
+ ip_set_id_t index;
+
+ index = ip_set_nfnl_get_byindex(info->match_set.index);
+
+ if (index == IPSET_INVALID_ID) {
+ pr_warning("Cannot find set indentified by id %u to match",
+ info->match_set.index);
+ return 0; /* error */
+ }
+ if (info->match_set.dim > IPSET_DIM_MAX) {
+ pr_warning("That's nasty!");
+ return 0; /* error */
+ }
+
+ return 1;
+}
+
+static void
+set_match_destroy(const struct xt_mtdtor_param *par)
+{
+ struct xt_set_info_match *info = par->matchinfo;
+
+
+ ip_set_nfnl_put(info->match_set.index);
+}
+
+/* Set target */
+
+static unsigned int
+set_target(struct sk_buff *skb, const struct xt_target_param *par)
+{
+ const struct xt_set_info_target *info = par->targinfo;
+
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_add(info->add_set.index,
+ skb, par->family,
+ info->add_set.dim,
+ info->add_set.flags);
+ if (info->del_set.index != IPSET_INVALID_ID)
+ ip_set_del(info->del_set.index,
+ skb, par->family,
+ info->add_set.dim,
+ info->del_set.flags);
+
+ return XT_CONTINUE;
+}
+
+static bool
+set_target_checkentry(const struct xt_tgchk_param *par)
+{
+ const struct xt_set_info_target *info = par->targinfo;
+ ip_set_id_t index;
+
+ if (info->add_set.index != IPSET_INVALID_ID) {
+ index = ip_set_nfnl_get_byindex(info->add_set.index);
+ if (index == IPSET_INVALID_ID) {
+ pr_warning("cannot find add_set index %u as target",
+ info->add_set.index);
+ return 0; /* error */
+ }
+ }
+
+ if (info->del_set.index != IPSET_INVALID_ID) {
+ index = ip_set_nfnl_get_byindex(info->del_set.index);
+ if (index == IPSET_INVALID_ID) {
+ pr_warning("cannot find del_set index %u as target",
+ info->del_set.index);
+ return 0; /* error */
+ }
+ }
+ if (info->add_set.dim > IPSET_DIM_MAX
+ || info->del_set.flags > IPSET_DIM_MAX) {
+ pr_warning("That's nasty!");
+ return 0; /* error */
+ }
+
+ return 1;
+}
+
+static void
+set_target_destroy(const struct xt_tgdtor_param *par)
+{
+ const struct xt_set_info_target *info = par->targinfo;
+
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->add_set.index);
+ if (info->del_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->del_set.index);
+}
+
+static struct xt_match set_matches[] __read_mostly = {
+ {
+ .name = "set",
+ .family = NFPROTO_IPV4,
+ .revision = 0,
+ .match = set_match_v0,
+ .matchsize = sizeof(struct xt_set_info_match_v0),
+ .checkentry = set_match_v0_checkentry,
+ .destroy = set_match_v0_destroy,
+ .me = THIS_MODULE
+ },
+ {
+ .name = "set",
+ .family = NFPROTO_IPV4,
+ .revision = 1,
+ .match = set_match,
+ .matchsize = sizeof(struct xt_set_info_match),
+ .checkentry = set_match_checkentry,
+ .destroy = set_match_destroy,
+ .me = THIS_MODULE
+ },
+ {
+ .name = "set",
+ .family = NFPROTO_IPV6,
+ .revision = 1,
+ .match = set_match,
+ .matchsize = sizeof(struct xt_set_info_match),
+ .checkentry = set_match_checkentry,
+ .destroy = set_match_destroy,
+ .me = THIS_MODULE
+ },
+};
+
+static struct xt_target set_targets[] __read_mostly = {
+ {
+ .name = "SET",
+ .revision = 0,
+ .family = NFPROTO_IPV4,
+ .target = set_target_v0,
+ .targetsize = sizeof(struct xt_set_info_target_v0),
+ .checkentry = set_target_v0_checkentry,
+ .destroy = set_target_v0_destroy,
+ .me = THIS_MODULE
+ },
+ {
+ .name = "SET",
+ .revision = 1,
+ .family = NFPROTO_IPV4,
+ .target = set_target,
+ .targetsize = sizeof(struct xt_set_info_target),
+ .checkentry = set_target_checkentry,
+ .destroy = set_target_destroy,
+ .me = THIS_MODULE
+ },
+ {
+ .name = "SET",
+ .revision = 1,
+ .family = NFPROTO_IPV6,
+ .target = set_target,
+ .targetsize = sizeof(struct xt_set_info_target),
+ .checkentry = set_target_checkentry,
+ .destroy = set_target_destroy,
+ .me = THIS_MODULE
+ },
+};
+
+static int __init xt_set_init(void)
+{
+ int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
+
+ if (!ret) {
+ ret = xt_register_targets(set_targets,
+ ARRAY_SIZE(set_targets));
+ if (ret)
+ xt_unregister_matches(set_matches,
+ ARRAY_SIZE(set_matches));
+ }
+ return ret;
+}
+
+static void __exit xt_set_fini(void)
+{
+ xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
+ xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
+}
+
+module_init(xt_set_init);
+module_exit(xt_set_fini);
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 74b6651..bf4e133 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -12,9 +12,7 @@ libipset_la_SOURCES = \
parse.c \
print.c \
session.c \
- types.c \
- utils.c
-
+ types.c
#%.o: %.c
# ${AM_VERBOSE_CC} ${CC} ${AM_DEPFLAGS} ${AM_CFLAGS} ${CFLAGS} -o $@ -c $<
diff --git a/lib/PROTOCOL b/lib/PROTOCOL
index e1a139e..6f07445 100644
--- a/lib/PROTOCOL
+++ b/lib/PROTOCOL
@@ -33,7 +33,11 @@ req: msg: IPSET_CMD_LIST|SAVE
attr: IPSET_ATTR_PROTOCOL
IPSET_ATTR_SETNAME (optional)
-resp: attr: IPSET_ATTR_DATA
+resp: attr: IPSET_ATTR_SETNAME
+ IPSET_ATTR_TYPENAME
+ IPSET_ATTR_REVISION
+ IPSET_ATTR_FAMILY
+ IPSET_ATTR_DATA
create-specific-data
IPSET_ATTR_ADT
IPSET_ATTR_DATA
diff --git a/lib/data.c b/lib/data.c
index 0de91a1..f8ff4a9 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -12,6 +12,7 @@
#include <string.h> /* memset */
#include <libipset/linux_ip_set.h> /* IPSET_MAXNAMELEN */
+#include <libipset/debug.h> /* D() */
#include <libipset/types.h> /* struct ipset_type */
#include <libipset/utils.h> /* inXcpy */
#include <libipset/data.h> /* prototypes */
@@ -26,13 +27,16 @@
struct ipset_data {
/* Option bits: which fields are set */
uint64_t bits;
+ /* Option bits: which options are ignored */
+ uint64_t ignored;
/* Setname */
char setname[IPSET_MAXNAMELEN];
const struct ipset_type *type;
/* Common CADT options */
uint8_t cidr;
uint8_t family;
- uint32_t flags;
+ uint32_t flags; /* command level flags */
+ uint32_t cadt_flags; /* data level flags */
uint32_t timeout;
union nf_inet_addr ip;
union nf_inet_addr ip_to;
@@ -79,6 +83,25 @@ copy_addr(uint8_t family, union nf_inet_addr *ip, const void *value)
}
/**
+ * ipset_strncpy - copy the string from src to dst
+ * @dst: the target string buffer
+ * @src: the source string buffer
+ * @len: the length of bytes to copy, including the terminating null byte.
+ *
+ * Copy the string from src to destination, but at most len bytes are
+ * copied. The target is unconditionally terminated by the null byte.
+ */
+void
+ipset_strncpy(char *dst, const char *src, size_t len)
+{
+ assert(dst);
+ assert(src);
+
+ strncpy(dst, src, len);
+ dst[len - 1] = '\0';
+}
+
+/**
* ipset_data_flags_test - test option bits in the data blob
* @data: data blob
* @flags: the option flags to test
@@ -122,13 +145,38 @@ ipset_data_flags_unset(struct ipset_data *data, uint64_t flags)
data->bits &= ~flags;
}
-#define flag_type_attr(data, opt, flag) \
-do { \
- data->flags |= (1 << flag); \
- opt = IPSET_OPT_FLAGS; \
+#define flag_type_attr(data, opt, flag) \
+do { \
+ data->flags |= flag; \
+ opt = IPSET_OPT_FLAGS; \
+} while (0)
+
+#define cadt_flag_type_attr(data, opt, flag) \
+do { \
+ data->cadt_flags |= flag; \
+ opt = IPSET_OPT_CADT_FLAGS; \
} while (0)
/**
+ * ipset_data_ignored - test and set ignored bits in the data blob
+ * @data: data blob
+ * @flags: the option flags which is ignored
+ *
+ * Returns true if the option was not already ignored.
+ */
+bool
+ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt)
+{
+ bool ignored;
+ assert(data);
+
+ ignored = data->ignored & IPSET_FLAG(opt);
+ data->ignored |= IPSET_FLAG(opt);
+
+ return ignored;
+}
+
+/**
* ipset_data_set - put data into the data blob
* @data: data blob
* @opt: the option kind of the data
@@ -249,11 +297,14 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
flag_type_attr(data, opt, IPSET_FLAG_EXIST);
break;
case IPSET_OPT_BEFORE:
- flag_type_attr(data, opt, IPSET_FLAG_BEFORE);
+ cadt_flag_type_attr(data, opt, IPSET_FLAG_BEFORE);
break;
case IPSET_OPT_FLAGS:
data->flags = *(const uint32_t *)value;
break;
+ case IPSET_OPT_CADT_FLAGS:
+ data->cadt_flags = *(const uint32_t *)value;
+ break;
default:
return -1;
};
@@ -351,8 +402,10 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
/* flags */
case IPSET_OPT_FLAGS:
case IPSET_OPT_EXIST:
- case IPSET_OPT_BEFORE:
return &data->flags;
+ case IPSET_OPT_CADT_FLAGS:
+ case IPSET_OPT_BEFORE:
+ return &data->cadt_flags;
default:
return NULL;
}
diff --git a/lib/mnl.c b/lib/mnl.c
index 5662a47..8056427 100644
--- a/lib/mnl.c
+++ b/lib/mnl.c
@@ -8,8 +8,10 @@
#include <errno.h> /* errno */
#include <stdlib.h> /* calloc, free */
#include <time.h> /* time */
+#include <arpa/inet.h> /* hto* */
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
+#include <libipset/debug.h> /* D() */
#include <libipset/session.h> /* ipset_session_handle */
#include <libipset/ui.h> /* IPSET_ENV_EXIST */
#include <libipset/utils.h> /* UNUSED */
diff --git a/lib/parse.c b/lib/parse.c
index 0e0e7f1..e347c69 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -13,6 +13,7 @@
#include <sys/socket.h> /* getaddrinfo, AF_ */
#include <net/ethernet.h> /* ETH_ALEN */
+#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* IPSET_OPT_* */
#include <libipset/pfxlen.h> /* prefixlen_netmask_map */
#include <libipset/session.h> /* ipset_err */
@@ -22,14 +23,31 @@
/* Parse input data */
-#define ipset_cidr_separator(str) ipset_strchr(str, IPSET_CIDR_SEPARATOR)
-#define ipset_range_separator(str) ipset_strchr(str, IPSET_RANGE_SEPARATOR)
-#define ipset_elem_separator(str) ipset_strchr(str, IPSET_ELEM_SEPARATOR)
-#define ipset_name_separator(str) ipset_strchr(str, IPSET_NAME_SEPARATOR)
+#define cidr_separator(str) ipset_strchr(str, IPSET_CIDR_SEPARATOR)
+#define range_separator(str) ipset_strchr(str, IPSET_RANGE_SEPARATOR)
+#define elem_separator(str) ipset_strchr(str, IPSET_ELEM_SEPARATOR)
+#define name_separator(str) ipset_strchr(str, IPSET_NAME_SEPARATOR)
#define syntax_err(fmt, args...) \
ipset_err(session, "Syntax error: " fmt , ## args)
+static char *
+ipset_strchr(const char *str, const char *sep)
+{
+ char *match;
+
+ assert(str);
+ assert(sep);
+
+ for (; *sep != '\0'; sep++)
+ if ((match = strchr(str, (int)sep[0])) != NULL
+ && str[0] != sep[0]
+ && str[strlen(str)-1] != sep[0])
+ return match;
+
+ return NULL;
+}
+
/*
* Parser functions, shamelessly taken from iptables.c, ip6tables.c
* and parser.c from libnetfilter_conntrack.
@@ -70,33 +88,53 @@ string_to_number_ll(struct ipset_session *session,
}
static int
-string_to_number_l(struct ipset_session *session,
- const char *str,
- unsigned long min,
- unsigned long max,
- unsigned long *ret)
+string_to_u8(struct ipset_session *session,
+ const char *str, uint8_t *ret)
{
int err;
- unsigned long long number = 0;
+ unsigned long long num = 0;
- err = string_to_number_ll(session, str, min, max, &number);
- *ret = (unsigned long) number;
+ err = string_to_number_ll(session, str, 0, 255, &num);
+ *ret = (uint8_t) num;
return err;
}
static int
-string_to_number(struct ipset_session *session,
- const char *str,
- unsigned int min,
- unsigned int max,
- unsigned int *ret)
+string_to_cidr(struct ipset_session *session,
+ const char *str, uint8_t min, uint8_t max, uint8_t *ret)
+{
+ int err = string_to_u8(session, str, ret);
+
+ if (!err && (*ret < min || *ret > max))
+ return syntax_err("'%s' is out of range %u-%u",
+ str, min, max);
+
+ return err;
+}
+
+static int
+string_to_u16(struct ipset_session *session,
+ const char *str, uint16_t *ret)
+{
+ int err;
+ unsigned long long num = 0;
+
+ err = string_to_number_ll(session, str, 0, USHRT_MAX, &num);
+ *ret = (uint16_t) num;
+
+ return err;
+}
+
+static int
+string_to_u32(struct ipset_session *session,
+ const char *str, uint32_t *ret)
{
int err;
- unsigned long number = 0;
+ unsigned long long num = 0;
- err = string_to_number_l(session, str, min, max, &number);
- *ret = (unsigned int) number;
+ err = string_to_number_ll(session, str, 0, UINT_MAX, &num);
+ *ret = (uint32_t) num;
return err;
}
@@ -161,12 +199,6 @@ parse_portname(struct ipset_session *session, const char *str, uint16_t *port)
return syntax_err("cannot parse '%s' as a (TCP) port", str);
}
-static int
-parse_portnum(struct ipset_session *session, const char *str, uint16_t *port)
-{
- return string_to_number(session, str, 0, 65535, (unsigned int *)port);
-}
-
/**
* ipset_parse_single_port - parse a single (TCP) port number or name
* @session: session structure
@@ -189,7 +221,7 @@ ipset_parse_single_port(struct ipset_session *session,
assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
assert(str);
- if ((err = parse_portnum(session, str, &port)) == 0
+ if ((err = string_to_u16(session, str, &port)) == 0
|| (err = parse_portname(session, str, &port)) == 0)
err = ipset_session_data_set(session, opt, &port);
@@ -229,7 +261,7 @@ ipset_parse_port(struct ipset_session *session,
"Cannot allocate memory to duplicate %s.",
str);
- a = ipset_range_separator(tmp);
+ a = range_separator(tmp);
if (a != NULL) {
/* port-port */
*a++ = '\0';
@@ -256,14 +288,20 @@ error:
* Returns 0 on success or a negative error code.
*/
int
-ipset_parse_family(struct ipset_session *session, int opt, const char *str)
+ipset_parse_family(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
{
+ struct ipset_data *data;
uint8_t family;
assert(session);
assert(opt == IPSET_OPT_FAMILY);
assert(str);
+ data = ipset_session_data(session);
+ if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FAMILY)))
+ syntax_err("protocol family may not be specified multiple times");
+
if (STREQ(str, "inet") || STREQ(str, "ipv4") || STREQ(str, "-4"))
family = AF_INET;
else if (STREQ(str, "inet6") || STREQ(str, "ipv6") || STREQ(str, "-6"))
@@ -273,7 +311,7 @@ ipset_parse_family(struct ipset_session *session, int opt, const char *str)
else
return syntax_err("unknown INET family %s", str);
- return ipset_session_data_set(session, opt, &family);
+ return ipset_data_set(data, opt, &family);
}
/*
@@ -316,8 +354,8 @@ get_addrinfo##f(struct ipset_session *session, \
int found; \
\
if ((*info = get_addrinfo(session, str, family)) == NULL) { \
- syntax_err("cannot parse %s: resolving " \
- IP " failed", str); \
+ syntax_err("cannot parse %s: " IP " resolving failed", \
+ str); \
return EINVAL; \
} \
\
@@ -347,7 +385,7 @@ static int \
parse_ipv##f(struct ipset_session *session, \
enum ipset_opt opt, const char *str) \
{ \
- unsigned int m = mask; \
+ uint8_t m = mask; \
int aerr = EINVAL, err = 0, range = 0; \
char *saved = strdup(str); \
char *a, *tmp = saved; \
@@ -361,14 +399,14 @@ parse_ipv##f(struct ipset_session *session, \
return ipset_err(session, \
"Cannot allocate memory to duplicate %s.",\
str); \
- if ((a = ipset_cidr_separator(tmp)) != NULL) { \
+ if ((a = cidr_separator(tmp)) != NULL) { \
/* IP/mask */ \
*a++ = '\0'; \
\
- if ((err = string_to_number(session, a, 0, m, &m)) != 0 \
+ if ((err = string_to_cidr(session, a, 0, m, &m)) != 0 \
|| (err = ipset_data_set(data, copt, &m)) != 0) \
goto out; \
- } else if ((a = ipset_range_separator(tmp)) != NULL) { \
+ } else if ((a = range_separator(tmp)) != NULL) { \
/* IP-IP */ \
*a++ = '\0'; \
D("range %s", a); \
@@ -420,17 +458,17 @@ parse_ip(struct ipset_session *session,
switch (addrtype) {
case IPADDR_PLAIN:
- if (ipset_range_separator(str) || ipset_cidr_separator(str))
+ if (range_separator(str) || cidr_separator(str))
return syntax_err("plain IP address must be supplied: %s",
str);
break;
case IPADDR_NET:
- if (!ipset_cidr_separator(str) || ipset_range_separator(str))
+ if (!cidr_separator(str) || range_separator(str))
return syntax_err("IP/netblock must be supplied: %s",
str);
break;
case IPADDR_RANGE:
- if (!ipset_range_separator(str) || ipset_cidr_separator(str))
+ if (!range_separator(str) || cidr_separator(str))
return syntax_err("IP-IP range must supplied: %s",
str);
break;
@@ -539,7 +577,7 @@ ipset_parse_range(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
assert(session);
- assert(opt == IPSET_OPT_IP);
+ assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
assert(str);
return parse_ip(session, IPSET_OPT_IP, str, IPADDR_RANGE);
@@ -563,15 +601,118 @@ ipset_parse_netrange(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
assert(session);
- assert(opt == IPSET_OPT_IP);
+ assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
assert(str);
- if (!(ipset_range_separator(str) || ipset_cidr_separator(str)))
- return syntax_err("IP/net or IP-IP range must be specified: %s",
+ if (!(range_separator(str) || cidr_separator(str)))
+ return syntax_err("IP/cidr or IP-IP range must be specified: %s",
str);
return parse_ip(session, opt, str, IPADDR_ANY);
}
+/**
+ * ipset_parse_iprange - parse IPv4|IPv6 address or range
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address pattern or a range
+ * of addresses separated by a dash. If family is not set yet in
+ * the data blob, INET is assumed.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_iprange(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ assert(session);
+ assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+ assert(str);
+
+ if (cidr_separator(str))
+ return syntax_err("IP address or IP-IP range must be specified: %s",
+ str);
+ return parse_ip(session, opt, str, IPADDR_ANY);
+}
+
+/**
+ * ipset_parse_ipnet - parse IPv4|IPv6 address or address/cidr pattern
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address or address/cidr pattern.
+ * If family is not set yet in the data blob, INET is assumed.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_ipnet(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ assert(session);
+ assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+ assert(str);
+
+ if (range_separator(str))
+ return syntax_err("IP address or IP/cidr must be specified: %s",
+ str);
+ return parse_ip(session, opt, str, IPADDR_ANY);
+}
+
+/**
+ * ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address and timeout parameter.
+ * If family is not set yet in the data blob, INET is assumed.
+ * The value is stored in the data blob of the session.
+ *
+ * Compatibility parser.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_iptimeout(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ char *tmp, *saved, *a;
+ int err;
+
+ assert(session);
+ assert(opt == IPSET_OPT_IP);
+ assert(str);
+
+ /* IP,timeout */
+ if (ipset_data_flags_test(ipset_session_data(session),
+ IPSET_FLAG(IPSET_OPT_TIMEOUT)))
+ return syntax_err("mixed syntax, timeout already specified");
+
+ tmp = saved = strdup(str);
+ if (saved == NULL)
+ return ipset_err(session,
+ "Cannot allocate memory to duplicate %s.",
+ str);
+
+ a = elem_separator(tmp);
+ if (a == NULL) {
+ free(saved);
+ return syntax_err("Missing separator from %s", str);
+ }
+ *a++ = '\0';
+ err = parse_ip(session, opt, tmp, IPADDR_ANY);
+ if (!err)
+ err = ipset_parse_uint32(session, IPSET_OPT_TIMEOUT, a);
+
+ free(saved);
+ return err;
+}
+
#define check_setname(str, saved) \
do { \
if (strlen(str) > IPSET_MAXNAMELEN - 1) { \
@@ -584,7 +725,7 @@ do { \
/**
- * ipset_parse_name - parse setname as element
+ * ipset_parse_name_compat - parse setname as element
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
@@ -597,8 +738,8 @@ do { \
* Returns 0 on success or a negative error code.
*/
int
-ipset_parse_name(struct ipset_session *session,
- enum ipset_opt opt, const char *str)
+ipset_parse_name_compat(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
{
char *saved;
char *a = NULL, *b = NULL, *tmp;
@@ -607,25 +748,22 @@ ipset_parse_name(struct ipset_session *session,
struct ipset_data *data;
assert(session);
- assert(opt == IPSET_OPT_NAME || opt == IPSET_OPT_SETNAME2);
+ assert(opt == IPSET_OPT_NAME);
assert(str);
data = ipset_session_data(session);
- if (opt == IPSET_OPT_SETNAME2) {
- check_setname(str, NULL);
-
- return ipset_data_set(data, opt, str);
- }
+ if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF)))
+ syntax_err("mixed syntax, before|after option already used");
tmp = saved = strdup(str);
if (saved == NULL)
return ipset_err(session,
"Cannot allocate memory to duplicate %s.",
str);
- if ((a = ipset_elem_separator(tmp)) != NULL) {
+ if ((a = elem_separator(tmp)) != NULL) {
/* setname,[before|after,setname */
*a++ = '\0';
- if ((b = ipset_elem_separator(a)) != NULL)
+ if ((b = elem_separator(a)) != NULL)
*b++ = '\0';
if (b == NULL
|| !(STREQ(a, "before") || STREQ(a, "after"))) {
@@ -644,7 +782,7 @@ ipset_parse_name(struct ipset_session *session,
if ((err = ipset_data_set(data,
IPSET_OPT_NAMEREF, b)) != 0)
goto out;
-
+
if (before)
err = ipset_data_set(data, IPSET_OPT_BEFORE, &before);
@@ -654,12 +792,12 @@ out:
}
/**
- * ipset_parse_setname - parse name as the name of the (current) set
+ * ipset_parse_setname - parse string as a setname
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
*
- * Parse string as the name of the (current) set.
+ * Parse string as a setname.
* The value is stored in the data blob of the session.
*
* Returns 0 on success or a negative error code.
@@ -669,7 +807,9 @@ ipset_parse_setname(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
assert(session);
- assert(opt == IPSET_SETNAME);
+ assert(opt == IPSET_SETNAME
+ || opt == IPSET_OPT_NAME
+ || opt == IPSET_OPT_SETNAME2);
assert(str);
check_setname(str, NULL);
@@ -678,6 +818,67 @@ ipset_parse_setname(struct ipset_session *session,
}
/**
+ * ipset_parse_before - parse string as "before" reference setname
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a "before" reference setname for list:set
+ * type of sets. The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_before(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ struct ipset_data *data;
+
+ assert(session);
+ assert(opt == IPSET_OPT_NAMEREF);
+ assert(str);
+
+ data = ipset_session_data(session);
+ if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF)))
+ syntax_err("mixed syntax, before|after option already used");
+
+ check_setname(str, NULL);
+ ipset_data_set(data, IPSET_OPT_BEFORE, str);
+
+ return ipset_data_set(data, opt, str);
+}
+
+/**
+ * ipset_parse_after - parse string as "after" reference setname
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a "after" reference setname for list:set
+ * type of sets. The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_after(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ struct ipset_data *data;
+
+ assert(session);
+ assert(opt == IPSET_OPT_NAMEREF);
+ assert(str);
+
+ data = ipset_session_data(session);
+ if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF)))
+ syntax_err("mixed syntax, before|after option already used");
+
+ check_setname(str, NULL);
+
+ return ipset_data_set(data, opt, str);
+}
+
+/**
* ipset_parse_uint32 - parse string as an unsigned integer
* @session: session structure
* @opt: option kind of the data
@@ -698,7 +899,7 @@ ipset_parse_uint32(struct ipset_session *session,
assert(session);
assert(str);
- if ((err = string_to_number(session, str, 0, 0, &value)) == 0)
+ if ((err = string_to_u32(session, str, &value)) == 0)
return ipset_session_data_set(session, opt, &value);
return err;
@@ -719,13 +920,13 @@ int
ipset_parse_uint8(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
- unsigned int value;
+ uint8_t value;
int err;
assert(session);
assert(str);
- if ((err = string_to_number(session, str, 0, 255, &value)) == 0)
+ if ((err = string_to_u8(session, str, &value)) == 0)
return ipset_session_data_set(session, opt, &value);
return err;
@@ -747,7 +948,7 @@ int
ipset_parse_netmask(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
- unsigned int family, cidr;
+ uint8_t family, cidr;
struct ipset_data *data;
int err = 0;
@@ -762,10 +963,10 @@ ipset_parse_netmask(struct ipset_session *session,
ipset_data_set(data, IPSET_OPT_FAMILY, &family);
}
- err = string_to_number(session, str,
- family == AF_INET ? 1 : 4,
- family == AF_INET ? 31 : 124,
- &cidr);
+ err = string_to_cidr(session, str,
+ family == AF_INET ? 1 : 4,
+ family == AF_INET ? 31 : 124,
+ &cidr);
if (err)
return syntax_err("netmask is out of the inclusive range "
@@ -864,15 +1065,72 @@ ipset_parse_output(struct ipset_session *session,
return syntax_err("unkown output mode '%s'", str);
}
+/**
+ * ipset_parse_ignored - "parse" ignored option
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Ignore deprecated options. A single warning is generated
+ * for every ignored opton.
+ *
+ * Returns 0.
+ */
+int
+ipset_parse_ignored(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ assert(session);
+ assert(str);
+
+ if (!ipset_data_ignored(ipset_session_data(session), opt))
+ ipset_warn(session,
+ "Option %s is ignored. Please upgrade your syntax.", str);
+
+ return 0;
+}
+
+/**
+ * ipset_call_parser - call a parser function
+ * @session: session structure
+ * @parsefn: parser function
+ * @optstr: option name
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Wrapper to call the parser functions so that ignored options
+ * are handled properly.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_call_parser(struct ipset_session *session,
+ ipset_parsefn parse, const char *optstr,
+ enum ipset_opt opt, const char *str)
+{
+ if (ipset_data_flags_test(ipset_session_data(session),
+ IPSET_FLAG(opt)))
+ syntax_err("%s already specified", optstr);
+
+ return parse(session, opt, parse == ipset_parse_ignored
+ ? optstr : str);
+}
+
#define parse_elem(s, t, d, str) \
do { \
- if (!t->elem[d].parse) \
+ if (!(t)->elem[d].parse) \
goto internal; \
- err = t->elem[d].parse(s, t->elem[d].opt, str); \
- if (err) \
+ ret = (t)->elem[d].parse(s, (t)->elem[d].opt, str); \
+ if (ret) \
goto out; \
} while (0)
+#define elem_syntax_err(fmt, args...) \
+do { \
+ free(saved); \
+ return syntax_err(fmt , ## args);\
+} while (0)
+
/**
* ipset_parse_elem - parse ADT elem, depending on settype
* @session: session structure
@@ -890,7 +1148,7 @@ ipset_parse_elem(struct ipset_session *session,
{
const struct ipset_type *type;
char *a = NULL, *b = NULL, *tmp, *saved;
- int err;
+ int ret;
assert(session);
assert(str);
@@ -906,40 +1164,43 @@ ipset_parse_elem(struct ipset_session *session,
"Cannot allocate memory to duplicate %s.",
str);
- a = ipset_elem_separator(tmp);
+ a = elem_separator(tmp);
if (type->dimension > IPSET_DIM_ONE) {
if (a != NULL) {
/* elem,elem */
*a++ = '\0';
- } else if (type->dimension > IPSET_DIM_TWO && !optional) {
- free(tmp);
- return syntax_err("Second element is missing from %s.",
- str);
+ } else if (!optional)
+ elem_syntax_err("Second element is missing from %s.",
+ str);
+ } else if (a != NULL) {
+ if (type->compat_parse_elem) {
+ ret = type->compat_parse_elem(session,
+ type->elem[IPSET_DIM_ONE].opt,
+ saved);
+ goto out;
}
- } else if (a != NULL)
- return syntax_err("Elem separator in %s, "
- "but settype %s supports none.",
- str, type->name);
+ elem_syntax_err("Elem separator in %s, "
+ "but settype %s supports none.",
+ str, type->name);
+ }
if (a)
- b = ipset_elem_separator(a);
+ b = elem_separator(a);
if (type->dimension > IPSET_DIM_TWO) {
if (b != NULL) {
/* elem,elem,elem */
*b++ = '\0';
- } else if (!optional) {
- free(tmp);
- return syntax_err("Third element is missing from %s.",
- str);
- }
+ } else if (!optional)
+ elem_syntax_err("Third element is missing from %s.",
+ str);
} else if (b != NULL)
- return syntax_err("Two elem separators in %s, "
- "but settype %s supports one.",
- str, type->name);
- if (b != NULL && ipset_elem_separator(b))
- return syntax_err("Three elem separators in %s, "
- "but settype %s supports two.",
- str, type->name);
+ elem_syntax_err("Two elem separators in %s, "
+ "but settype %s supports one.",
+ str, type->name);
+ if (b != NULL && elem_separator(b))
+ elem_syntax_err("Three elem separators in %s, "
+ "but settype %s supports two.",
+ str, type->name);
D("parse elem part one: %s", tmp);
parse_elem(session, type, IPSET_DIM_ONE, tmp);
@@ -954,10 +1215,10 @@ ipset_parse_elem(struct ipset_session *session,
goto out;
internal:
- err = ipset_err(session,
+ ret = ipset_err(session,
"Internal error: missing parser function for %s",
type->name);
out:
free(saved);
- return err;
+ return ret;
}
diff --git a/lib/print.c b/lib/print.c
index 4df0905..d96e643 100644
--- a/lib/print.c
+++ b/lib/print.c
@@ -13,6 +13,7 @@
#include <arpa/inet.h> /* inet_ntop */
#include <net/ethernet.h> /* ETH_ALEN */
+#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_* */
#include <libipset/parse.h> /* IPSET_*_SEPARATOR */
#include <libipset/types.h> /* ipset set types */
@@ -86,7 +87,7 @@ ipset_print_ether(char *buf, unsigned int len,
*/
int
ipset_print_family(char *buf, unsigned int len,
- const struct ipset_data *data, int opt,
+ const struct ipset_data *data, enum ipset_opt opt,
uint8_t env UNUSED)
{
uint8_t family;
@@ -172,10 +173,10 @@ snprintf_ipv##f(char *buf, unsigned int len, int flags, \
size = __getnameinfo##f(buf, len, flags, ip); \
SNPRINTF_FAILURE(size, len, offset); \
\
+ D("cidr %u mask %u", cidr, mask); \
if (cidr == mask) \
return offset; \
- if ((unsigned int)(size + 5) < len) \
- return -1; \
+ D("print cidr"); \
size = snprintf(buf + offset, len, \
"%s%u", IPSET_CIDR_SEPARATOR, cidr); \
SNPRINTF_FAILURE(size, len, offset); \
@@ -218,9 +219,10 @@ ipset_print_ip(char *buf, unsigned int len,
D("len: %u", len);
family = ipset_data_family(data);
cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
- if (ipset_data_test(data, cidropt))
+ if (ipset_data_test(data, cidropt)) {
cidr = *(uint8_t *) ipset_data_get(data, cidropt);
- else
+ D("CIDR: %u", cidr);
+ } else
cidr = family == AF_INET6 ? 128 : 32;
flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
@@ -373,11 +375,14 @@ ipset_print_name(char *buf, unsigned int len,
SNPRINTF_FAILURE(size, len, offset);
if (ipset_data_test(data, IPSET_OPT_NAMEREF)) {
- bool before = ipset_data_test(data, IPSET_OPT_BEFORE);
+ bool before = false;
+ if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FLAGS))) {
+ uint32_t *flags =
+ (uint32_t *)ipset_data_get(data, IPSET_OPT_FLAGS);
+ before = (*flags) & IPSET_FLAG_BEFORE;
+ }
size = snprintf(buf + offset, len,
- "%s%s%s%s", IPSET_ELEM_SEPARATOR,
- before ? "before" : "after",
- IPSET_ELEM_SEPARATOR,
+ " %s %s", before ? "before" : "after",
(const char *) ipset_data_get(data,
IPSET_OPT_NAMEREF));
SNPRINTF_FAILURE(size, len, offset);
@@ -468,8 +473,8 @@ ipset_print_elem(char *buf, unsigned int len,
size = type->elem[IPSET_DIM_ONE].print(buf, len, data,
type->elem[IPSET_DIM_ONE].opt, env);
SNPRINTF_FAILURE(size, len, offset);
- if (ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt))
- D("print second elem");
+ IF_D(ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt),
+ "print second elem");
if (type->dimension == IPSET_DIM_ONE
|| (type->last_elem_optional
&& !ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt)))
diff --git a/lib/session.c b/lib/session.c
index 2c4e39a..2c85468 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -13,6 +13,7 @@
#include <unistd.h> /* getpagesize */
#include <net/ethernet.h> /* ETH_ALEN */
+#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* IPSET_OPT_* */
#include <libipset/errcode.h> /* ipset_errcode */
#include <libipset/print.h> /* ipset_print_* */
@@ -189,6 +190,8 @@ ipset_session_report(struct ipset_session *session,
if (len >= IPSET_ERRORBUFLEN - 1 - offset)
session->report[IPSET_ERRORBUFLEN - 1] = '\0';
+ if (strlen(session->report) < IPSET_ERRORBUFLEN - 1)
+ strcat(session->report, "\n");
if (type == IPSET_ERROR) {
session->errmsg = session->report;
@@ -278,6 +281,10 @@ const struct ipset_attr_policy cmd_attrs[] = {
.type = MNL_TYPE_U8,
.opt = IPSET_OPT_FAMILY,
},
+ [IPSET_ATTR_FLAGS] = {
+ .type = MNL_TYPE_U32,
+ .opt = IPSET_OPT_FLAGS,
+ },
[IPSET_ATTR_DATA] = {
.type = MNL_TYPE_NESTED,
},
@@ -320,9 +327,9 @@ const struct ipset_attr_policy create_attrs[] = {
.type = MNL_TYPE_U32,
.opt = IPSET_OPT_TIMEOUT,
},
- [IPSET_ATTR_FLAGS] = {
+ [IPSET_ATTR_CADT_FLAGS] = {
.type = MNL_TYPE_U32,
- .opt = IPSET_OPT_FLAGS,
+ .opt = IPSET_OPT_CADT_FLAGS,
},
[IPSET_ATTR_GC] = {
.type = MNL_TYPE_U32,
@@ -391,9 +398,9 @@ const struct ipset_attr_policy adt_attrs[] = {
.type = MNL_TYPE_U32,
.opt = IPSET_OPT_TIMEOUT,
},
- [IPSET_ATTR_FLAGS] = {
+ [IPSET_ATTR_CADT_FLAGS] = {
.type = MNL_TYPE_U32,
- .opt = IPSET_OPT_FLAGS,
+ .opt = IPSET_OPT_CADT_FLAGS,
},
[IPSET_ATTR_LINENO] = {
.type = MNL_TYPE_U32,
@@ -434,6 +441,7 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
struct ipset_data *data = session->data;
const struct ipset_attr_policy *attr;
const void *d;
+ int ret;
attr = &attrs[type];
d = mnl_attr_get_payload(nla[type]);
@@ -480,7 +488,14 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
break;
}
}
- return ipset_data_set(data, attr->opt, d);
+ if (type == IPSET_ATTR_TYPENAME)
+ D("nla typename %s", (char *) d);
+ ret = ipset_data_set(data, attr->opt, d);
+ if (type == IPSET_ATTR_TYPENAME)
+ D("nla typename %s",
+ (char *) ipset_data_get(data, IPSET_OPT_TYPENAME));
+
+ return ret;
}
#define ATTR2DATA(session, nla, type, attrs) \
@@ -579,8 +594,9 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
const struct ipset_type *type;
const struct ipset_arg *arg;
uint8_t family;
- int i;
-
+ int i, found = 0;
+
+ D("enter");
/* Check and load type, family */
if (!ipset_data_test(data, IPSET_OPT_TYPE))
type = ipset_type_get(session, IPSET_CMD_ADD);
@@ -591,6 +607,15 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
return MNL_CB_ERROR;
family = ipset_data_family(data);
+ for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
+ if (nla[i]) {
+ found++;
+ ATTR2DATA(session, nla, i, adt_attrs);
+ }
+ D("attr found %u", found);
+ if (!found)
+ return MNL_CB_OK;
+
switch (session->mode) {
case IPSET_LIST_SAVE:
safe_snprintf(session, "add %s ", ipset_data_setname(data));
@@ -603,10 +628,6 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
break;
}
- for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
- if (nla[i])
- ATTR2DATA(session, nla, i, adt_attrs);
-
safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM);
for (arg = type->args[IPSET_ADD]; arg != NULL && arg->print; arg++) {
@@ -646,6 +667,10 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
return MNL_CB_OK;
}
+#define FAMILY_TO_STR(f) \
+ ((f) == AF_INET ? "inet" : \
+ (f) == AF_INET6 ? "inet6" : "any")
+
static int
list_create(struct ipset_session *session, struct nlattr *nla[])
{
@@ -671,33 +696,29 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
safe_snprintf(session, "create %s %s ",
ipset_data_setname(data),
type->name);
- if (family == AF_INET6)
- sprintf(session->outbuf, "family inet6 ");
break;
case IPSET_LIST_PLAIN:
- safe_snprintf(session, "Name: %s\nType: %s\n",
+ safe_snprintf(session, "Name: %s\n"
+ "Type: %s\nHeader: ",
ipset_data_setname(data),
type->name);
- if (family == AF_INET6)
- safe_snprintf(session, "Family: INET6\n");
- safe_snprintf(session, "Header: ");
break;
case IPSET_LIST_XML:
safe_snprintf(session,
"<ipset name=\"%s\">\n"
- " <type>%s</type>\n",
+ " <type>%s</type>\n"
+ " <header>\n",
ipset_data_setname(data),
type->name);
- if (family == AF_INET6)
- safe_snprintf(session, " <family>INET6</family>\n");
- safe_snprintf(session, " <header>\n");
break;
default:
break;
}
- for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->print; arg++) {
- if (!ipset_data_test(data, arg->opt))
+ for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->opt; arg++) {
+ if (!arg->print
+ || !ipset_data_test(data, arg->opt)
+ || (arg->opt == IPSET_OPT_FAMILY && family == type->family))
continue;
switch (session->mode) {
case IPSET_LIST_SAVE:
@@ -730,8 +751,6 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
safe_snprintf(session, "\n");
break;
case IPSET_LIST_PLAIN:
- safe_snprintf(session, "\nElements: ");
- safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
safe_snprintf(session, "\nSize in memory: ");
safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
safe_snprintf(session, "\nReferences: ");
@@ -739,8 +758,6 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
safe_snprintf(session, "\nMembers:\n");
break;
case IPSET_LIST_XML:
- safe_snprintf(session, " <elements>");
- safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
safe_snprintf(session, "</elements>\n <memsize>");
safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
safe_snprintf(session, "</memsize>\n <references>");
@@ -757,7 +774,8 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
static int
print_set_done(struct ipset_session *session)
{
- D("called");
+ D("called for %s", session->saved_setname[0] == '\0'
+ ? "NONE" : session->saved_setname);
switch (session->mode) {
case IPSET_LIST_XML:
if (session->saved_setname[0] == '\0')
@@ -824,6 +842,7 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
cmd2name[cmd]);
ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs);
+ D("setname %s", ipset_data_setname(data));
if (STREQ(ipset_data_setname(data), session->saved_setname)) {
/* Header part already seen */
if (ipset_data_test(data, IPSET_OPT_TYPE)
@@ -851,12 +870,18 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
!nla[IPSET_ATTR_TYPENAME] ? "typename" :
!nla[IPSET_ATTR_FAMILY] ? "family" : "revision");
+ /* Reset CREATE specific flags */
+ ipset_data_flags_unset(data, IPSET_CREATE_FLAGS);
+ D("nla typename %s",
+ (char *) mnl_attr_get_payload(nla[IPSET_ATTR_TYPENAME]));
+ D("nla typename %s",
+ (char *) mnl_attr_get_payload(nla[IPSET_ATTR_TYPENAME]));
ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
-
- /* Reset CREATE specific flags */
- ipset_data_flags_unset(data, IPSET_CREATE_FLAGS);
+ D("head: family %u, typename %s",
+ ipset_data_family(data),
+ (char *) ipset_data_get(data, IPSET_OPT_TYPENAME));
if (mnl_attr_parse_nested(nla[IPSET_ATTR_DATA],
create_attr_cb, cattr) < 0)
FAILURE("Broken %s kernel message: "
@@ -869,8 +894,9 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
if (nla[IPSET_ATTR_ADT] != NULL) {
struct nlattr *tb, *adt[IPSET_ATTR_ADT_MAX+1];
-
+
mnl_attr_for_each_nested(tb, nla[IPSET_ATTR_ADT]) {
+ D("ADT attributes for %s", ipset_data_setname(data));
memset(adt, 0, sizeof(adt));
/* Reset ADT specific flags */
ipset_data_flags_unset(data, IPSET_ADT_FLAGS);
@@ -950,6 +976,7 @@ callback_header(struct ipset_session *session, struct nlattr *nla[])
ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
+ D("got family: %u", ipset_data_family(session->data));
return MNL_CB_STOP;
}
@@ -1150,7 +1177,8 @@ callback_error(const struct nlmsghdr *nlh, void *cbdata)
case IPSET_CMD_CREATE:
/* Add successfully created set to the cache */
ipset_cache_add(ipset_data_setname(data),
- ipset_data_get(data, IPSET_OPT_TYPE));
+ ipset_data_get(data, IPSET_OPT_TYPE),
+ ipset_data_family(data));
break;
case IPSET_CMD_DESTROY:
/* Delete destroyed sets from the cache */
@@ -1750,7 +1778,7 @@ ipset_session_init(ipset_outfn outfn)
if (session->data == NULL)
goto free_session;
- ipset_types_init();
+ ipset_cache_init();
return session;
free_session:
@@ -1776,7 +1804,7 @@ ipset_session_fini(struct ipset_session *session)
if (session->data)
ipset_data_fini(session->data);
- ipset_types_fini();
+ ipset_cache_fini();
free(session);
return 0;
}
diff --git a/lib/types.c b/lib/types.c
index a6476ea..b39a04f 100644
--- a/lib/types.c
+++ b/lib/types.c
@@ -13,6 +13,7 @@
#include <stdlib.h> /* malloc, free */
#include <stdio.h> /* FIXME: debug */
+#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_* */
#include <libipset/session.h> /* ipset_cmd */
#include <libipset/utils.h> /* STREQ */
@@ -23,6 +24,7 @@
struct ipset {
char name[IPSET_MAXNAMELEN]; /* set name */
const struct ipset_type *type; /* set type */
+ uint8_t family; /* family */
struct ipset *next;
};
@@ -40,7 +42,8 @@ static struct ipset *setlist = NULL; /* cached sets */
* Returns 0 on success or a negative error code.
*/
int
-ipset_cache_add(const char *name, const struct ipset_type *type)
+ipset_cache_add(const char *name, const struct ipset_type *type,
+ uint8_t family)
{
struct ipset *s, *n;
@@ -53,13 +56,14 @@ ipset_cache_add(const char *name, const struct ipset_type *type)
ipset_strncpy(n->name, name, IPSET_MAXNAMELEN);
n->type = type;
+ n->family = family;
n->next = NULL;
if (setlist == NULL) {
setlist = n;
return 0;
}
- for (s = setlist; s->next == NULL; s = s->next) {
+ for (s = setlist; s->next != NULL; s = s->next) {
if (STREQ(name, s->name)) {
free(n);
return -EEXIST;
@@ -171,6 +175,22 @@ ipset_cache_swap(const char *from, const char *to)
#define MATCH_FAMILY(type, f) \
(f == AF_UNSPEC || type->family == f || type->family == AF_INET46)
+bool
+ipset_match_typename(const char *name, const struct ipset_type *type)
+{
+ const char * const * alias = type->alias;
+
+ if (STREQ(name, type->name))
+ return true;
+
+ while (alias[0]) {
+ if (STREQ(name, alias[0]))
+ return true;
+ alias++;
+ }
+ return false;
+}
+
static inline const struct ipset_type *
create_type_get(struct ipset_session *session)
{
@@ -192,7 +212,7 @@ create_type_get(struct ipset_session *session)
/* Skip revisions which are unsupported by the kernel */
if (t->kernel_check == IPSET_KERNEL_MISMATCH)
continue;
- if ((STREQ(typename, t->name) || STREQ(typename, t->alias))
+ if (ipset_match_typename(typename, t)
&& MATCH_FAMILY(t, family)) {
if (match == NULL) {
match = t;
@@ -390,8 +410,9 @@ ipset_type_check(struct ipset_session *session)
for (t = typelist; t != NULL && match == NULL; t = t->next) {
if (t->kernel_check == IPSET_KERNEL_MISMATCH)
continue;
- if ((STREQ(typename, t->name) || STREQ(typename, t->alias))
- && MATCH_FAMILY(t, family) && t->revision == revision)
+ if (ipset_match_typename(typename, t)
+ && MATCH_FAMILY(t, family)
+ && t->revision == revision)
match = t;
}
if (!match)
@@ -503,7 +524,7 @@ ipset_typename_resolve(const char *str)
const struct ipset_type *t;
for (t = typelist; t != NULL; t = t->next)
- if (STREQ(str, t->name) || STREQ(str, t->alias))
+ if (ipset_match_typename(str, t))
return t->name;
return NULL;
}
@@ -523,38 +544,25 @@ ipset_types(void)
}
/**
- * ipset_types_init - initialize known set types
+ * ipset_cache_init - initialize set cache
*
- * Initialize the type list with the known, supported set types.
+ * Initialize the set cache in userspace.
*
* Returns 0 on success or a negative error code.
*/
int
-ipset_types_init(void)
+ipset_cache_init(void)
{
- if (typelist != NULL)
- return 0;
-
- ipset_type_add(&ipset_bitmap_ip0);
- ipset_type_add(&ipset_bitmap_ipmac0);
- ipset_type_add(&ipset_bitmap_port0);
- ipset_type_add(&ipset_hash_ip0);
- ipset_type_add(&ipset_hash_net0);
- ipset_type_add(&ipset_hash_ipport0);
- ipset_type_add(&ipset_hash_ipportip0);
- ipset_type_add(&ipset_hash_ipportnet0);
- ipset_type_add(&ipset_tree_ip0);
- ipset_type_add(&ipset_list_set0);
return 0;
}
/**
- * ipset_types_fini - release initialized known set types
+ * ipset_cache_fini - release the set cache
*
- * Release initialized known set types and remove the set cache.
+ * Release the set cache.
*/
void
-ipset_types_fini(void)
+ipset_cache_fini(void)
{
struct ipset *set;
diff --git a/lib/utils.c b/lib/utils.c
deleted file mode 100644
index bddeb87..0000000
--- a/lib/utils.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/* Copyright 2007-20010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <assert.h> /* assert */
-#include <stdbool.h> /* bool */
-#include <stdlib.h> /* malloc, free */
-#include <string.h> /* memset, str* */
-
-#include <libipset/session.h> /* ipset_err */
-#include <libipset/utils.h> /* prototypes */
-
-/**
- * ipset_strchr - locate character(s) in string
- * @str: string to locate the character(s) in
- * @sep: string of characters to locate
- *
- * Return a pointer to the first occurence of any of the
- * characters to be located in the string. NULL is returned
- * if no character is found.
- */
-char *
-ipset_strchr(const char *str, const char *sep)
-{
- char *match;
-
- assert(str);
- assert(sep);
-
- for (; *sep != '\0'; sep++)
- if ((match = strchr(str, (int)sep[0])) != NULL)
- return match;
-
- return NULL;
-}
-
-/**
- * ipset_name_match - match a string against an array of strings
- * @arg: string
- * @name: array of strings, last one is a NULL pointer
- *
- * Return true if arg matches any of the strings in the array.
- */
-bool
-ipset_name_match(const char *arg, const char * const name[])
-{
- int i = 0;
-
- assert(arg);
- assert(name);
-
- while (name[i]) {
- if (STREQ(arg, name[i]))
- return true;
- i++;
- }
-
- return false;
-}
-
-/**
- * ipset_shift_argv - shift off an argument
- * @arc: argument count
- * @argv: array of argument strings
- * @from: from where shift off an argument
- *
- * Shift off the argument at "from" from the array of
- * arguments argv of size argc.
- */
-void
-ipset_shift_argv(int *argc, char *argv[], int from)
-{
- int i;
-
- assert(*argc >= from + 1);
-
- for (i = from + 1; i <= *argc; i++) {
- argv[i-1] = argv[i];
- }
- (*argc)--;
- return;
-}
-
-/**
- * ipset_strncpy - copy the string from src to dst
- * @dst: the target string buffer
- * @src: the source string buffer
- * @len: the length of bytes to copy, including the terminating null byte.
- *
- * Copy the string from src to destination, but at most len bytes are
- * copied. The target is unconditionally terminated by the null byte.
- */
-void
-ipset_strncpy(char *dst, const char *src, size_t len)
-{
- assert(dst);
- assert(src);
-
- strncpy(dst, src, len);
- dst[len - 1] = '\0';
-}
diff --git a/netlink.patch b/netlink.patch
index 685cd23..344cfc8 100644
--- a/netlink.patch
+++ b/netlink.patch
@@ -37,7 +37,7 @@ index ab5d312..ef8b229 100644
#define NL_NONROOT_RECV 0x1
#define NL_NONROOT_SEND 0x2
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
-index 19e9800..1b9dbe8 100644
+index 19e9800..7d85d45 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1714,15 +1714,18 @@ errout:
@@ -64,17 +64,17 @@ index 19e9800..1b9dbe8 100644
cb = kzalloc(sizeof(*cb), GFP_KERNEL);
if (cb == NULL)
-@@ -1748,6 +1751,10 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
- sock_put(sk);
- return -EBUSY;
- }
+@@ -1733,6 +1736,10 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
+ cb->nlh = nlh;
+ atomic_inc(&skb->users);
+ cb->skb = skb;
+ va_start(args, init);
+ for (i = 0; i < init; i++)
-+ cb->args[i] = va_arg(args, unsigned long);
++ cb->args[i] = va_arg(args, long);
+ va_end(args);
- nlk->cb = cb;
- mutex_unlock(nlk->cb_mutex);
+ sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).pid);
+ if (sk == NULL) {
@@ -1759,7 +1766,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
*/
return -EINTR;
diff --git a/src/Makefile.am b/src/Makefile.am
index 28b28fa..ebd08a3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,7 +12,6 @@ ipset_SOURCES = ipset.c \
ipset_hash_ipportnet.c \
ipset_hash_net.c \
ipset_list_set.c \
- ipset_tree_ip.c \
ui.c
ipset_LDADD = ../lib/libipset.la
AM_LDFLAGS = -static
diff --git a/src/errcode.c b/src/errcode.c
index 34b87a3..c30ecb6 100644
--- a/src/errcode.c
+++ b/src/errcode.c
@@ -8,6 +8,7 @@
#include <errno.h> /* errno */
#include <string.h> /* strerror */
+#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_get */
#include <libipset/session.h> /* ipset_err */
#include <libipset/types.h> /* struct ipset_type */
@@ -15,6 +16,7 @@
#include <libipset/errcode.h> /* prototypes */
#include <libipset/linux_ip_set_bitmap.h> /* bitmap specific errcodes */
#include <libipset/linux_ip_set_hash.h> /* hash specific errcodes */
+#include <libipset/linux_ip_set_list.h> /* list specific errcodes */
/* Core kernel error codes */
static const struct ipset_errcode_table core_errcode_table[] = {
@@ -45,6 +47,8 @@ static const struct ipset_errcode_table core_errcode_table[] = {
/* RENAME specific error codes */
{ IPSET_ERR_EXIST_SETNAME2, IPSET_CMD_RENAME,
"Set cannot be renamed: a set with the new name already exists" },
+ { IPSET_ERR_REFERENCED, IPSET_CMD_RENAME,
+ "Set cannot be renamed: it is in use by another system" },
/* SWAP specific error codes */
{ IPSET_ERR_EXIST_SETNAME2, IPSET_CMD_SWAP,
@@ -101,6 +105,24 @@ static const struct ipset_errcode_table hash_errcode_table[] = {
{ },
};
+/* List type-specific error codes */
+static const struct ipset_errcode_table list_errcode_table[] = {
+ /* Generic (CADT) error codes */
+ { IPSET_ERR_NAME, 0,
+ "Set to be added/deleted/tested as element does not exist." },
+ { IPSET_ERR_LOOP, 0,
+ "Sets with list:set type cannot be added to the set." },
+ { IPSET_ERR_BEFORE, 0,
+ "No reference set specified." },
+ { IPSET_ERR_NAMEREF, 0,
+ "The set to which you referred with 'before' or 'after' does not exist." },
+ { IPSET_ERR_LIST_FULL, 0,
+ "The set is full, more elements cannot be added." },
+ { IPSET_ERR_REF_EXIST, 0,
+ "The set to which you referred with 'before' or 'after' is not added to the set." },
+ { },
+};
+
#define MATCH_TYPENAME(a, b) STRNEQ(a, b, strlen(b))
/**
@@ -126,8 +148,10 @@ ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode)
if (type) {
if (MATCH_TYPENAME(type->name, "bitmap:"))
table = bitmap_errcode_table;
- if (MATCH_TYPENAME(type->name, "hash:"))
+ else if (MATCH_TYPENAME(type->name, "hash:"))
table = hash_errcode_table;
+ else if (MATCH_TYPENAME(type->name, "list:"))
+ table = list_errcode_table;
}
}
diff --git a/src/ipset.8 b/src/ipset.8
index fa73298..661d1b4 100644
--- a/src/ipset.8
+++ b/src/ipset.8
@@ -1,537 +1,628 @@
-.TH IPSET 8 "Feb 05, 2004" "" ""
-.\"
.\" Man page written by Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
-.\"
-.\" This program is free software; you can redistribute it and/or modify
-.\" it under the terms of the GNU General Public License as published by
-.\" the Free Software Foundation; either version 2 of the License, or
-.\" (at your option) any later version.
-.\"
-.\" This program is distributed in the hope that it will be useful,
-.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
-.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-.\" GNU General Public License for more details.
-.\"
-.\" You should have received a copy of the GNU General Public License
-.\" along with this program; if not, write to the Free Software
-.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-.\"
-.\"
-.SH NAME
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.TH "IPSET" "8" "Jun 11, 2010" "Jozsef Kadlecsik" ""
+.SH "NAME"
ipset \(em administration tool for IP sets
-.SH SYNOPSIS
-.PP
-\fBipset \-N\fP \fIset\fP \fItype-specification\fP [\fIoptions\fP...]
-.PP
-\fBipset\fP {\fB\-F\fP|\fB\-H\fP|\fB\-L\fP|\fB\-S\fP|\fB\-X\fP} [\fIset\fP]
-[\fIoptions\fP...]
-.PP
-\fBipset\fP {\fB\-E\fP|\fB\-W\fP} \fIfrom-set\fP \fIto-set\fP
-.PP
-\fBipset\fP {\fB\-A\fP|\fB\-D\fP|\fB\-T\fP} \fIset\fP \fIentry\fP
-.PP
-\fBipset \-R\fP
-.PP
-\fBipset\fP {\fB-V\fP|\fB\-v\fP}
-.SH DESCRIPTION
-.B ipset
+.SH "SYNOPSIS"
+\fBipset\fR [ \fIOPTIONS\fR ] \fICOMMAND\fR [ \fICOMMAND\-OPTIONS\fR ]
+.PP
+COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR | \fBlist\fR | \fBsave\fR | \fBrestore\fR | \fBflush\fR | \fBrename\fR | \fBswap\fR | \fBhelp\fR | \fBversion\fR | \fB\-\fR }
+.PP
+\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR }
+.PP
+\fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ]
+.PP
+\fBipset\fR \fBadd\fR \fISETNAME\fR \fIADD\-ENTRY\fR [ \fIADD\-OPTIONS\fR ]
+.PP
+\fBipset\fR \fBdel\fR \fISETNAME\fR \fIDEL\-ENTRY\fR [ \fIDEL\-OPTIONS\fR ]
+.PP
+\fBipset\fR \fBtest\fR \fISETNAME\fR \fITEST\-ENTRY\fR [ \fITEST\-OPTIONS\fR ]
+.PP
+\fBipset\fR \fBdestroy\fR [ \fISETNAME\fR ]
+.PP
+\fBipset\fR \fBlist\fR [ \fISETNAME\fR ]
+.PP
+\fBipset\fR \fBsave\fR [ \fISETNAME\fR ]
+.PP
+\fBipset\fR \fBrestore\fR
+.PP
+\fBipset\fR \fBflush\fR [ \fISETNAME\fR ]
+.PP
+\fBipset\fR \fBrename\fR \fISETNAME\-FROM\fR \fISETNAME\-TO\fR
+.PP
+\fBipset\fR \fBswap\fR \fISETNAME\-FROM\fR \fISETNAME\-TO\fR
+.PP
+\fBipset\fR \fBhelp\fR [ \fITYPENAME\fR ]
+.PP
+\fBipset\fR \fBversion\fR
+.PP
+\fBipset\fR \fB\-\fR
+.SH "DESCRIPTION"
+\fBipset\fR
is used to set up, maintain and inspect so called IP sets in the Linux
-kernel. Depending on the type, an IP set may store IP addresses, (TCP/UDP)
-port numbers or additional informations besides IP addresses: the word IP
-means a general term here. See the set type definitions below.
-.P
-Iptables matches and targets referring to sets creates references, which
-protects the given sets in the kernel. A set cannot be removed (destroyed)
+kernel. Depending on the type of the set, an IP set may store IP(v4/v6)
+addresses, (TCP/UDP) port numbers, IP and MAC address pairs, IP address
+and port number pairs, etc. See the set type definitions below.
+.PP
+\fBIptables\fR
+matches and targets referring to sets creates references, which
+protect the given sets in the kernel. A set cannot be destroyed
while there is a single reference pointing to it.
-.SH OPTIONS
+.SH "OPTIONS"
The options that are recognized by
-.B ipset
+\fBipset\fR
can be divided into several different groups.
.SS COMMANDS
-These options specify the specific action to perform. Only one of them
-can be specified on the command line unless otherwise specified
-below. For all the long versions of the command and option names, you
-need to use only enough letters to ensure that
-.B ipset
-can differentiate it from all other options.
-.TP
-\fB\-N\fP, \fB\-\-create\fP \fIsetname\fP \fItype\fP \fItype-specific-options\fP
-Create a set identified with setname and specified type.
-Type-specific options must be supplied.
-.TP
-\fB\-X\fP, \fB\-\-destroy\fP [\fIsetname\fP]
+These options specify the desired action to perform. Only one of them
+can be specified on the command line unless otherwise specified below.
+For all the long versions of the command names, you need to use only enough
+letters to ensure that
+\fBipset\fR
+can differentiate it from all other options. The
+\fBipset\fR
+parser follows the order here when looking for the shortest match
+in the long command names.
+.TP
+\fBn\fP, \fBcreate\fP \fISETNAME\fP \fITYPENAME\fP [ \fICREATE\-OPTIONS\fP ]
+Create a set identified with setname and specified type. The type may require
+type specific options. If the
+\fB\-exist\fR
+option is specified,
+\fBipset\fR
+ignores the error otherwise raised when the the same set (setname and create parameters
+are identical) already exists.
+.TP
+\fBadd\fP \fISETNAME\fP \fIADD\-ENTRY\fP [ \fIADD\-OPTIONS\fP ]
+Add a given entry to the set. If the
+\fB\-exist\fR
+option is specified,
+\fBipset\fR
+ignores if the entry already added to the set.
+.TP
+\fBdel\fP \fISETNAME\fP \fIDEL\-ENTRY\fP [ \fIDEL\-OPTIONS\fP ]
+Delete an entry from a set. If the
+\fB\-exist\fR
+option is specified,
+\fBipset\fR
+ignores if the entry does not added (expired) to the set.
+.TP
+\fBtest\fP \fISETNAME\fP \fITEST\-ENTRY\fP [ \fITEST\-OPTIONS\fP ]
+Test wether an entry is in a set or not. Exit status number is zero
+if the tested entry is in the set and nonzero if it is missing from
+the set.
+.TP
+\fBx\fP, \fBdestroy\fP [ \fISETNAME\fP ]
Destroy the specified set or all the sets if none is given.
-If the set has got references, nothing is done.
-.TP
-\fB\-F\fP, \fB\-\-flush\fP [\fIsetname\fP]
-Delete all entries from the specified set or flush
-all sets if none is given.
-.TP
-\fB\-E\fP, \fB\-\-rename\fP \fIfrom-setname\fP \fIto-setname\fP
-Rename a set. Set identified by to-setname must not exist.
-.TP
-\fB\-W\fP, \fB\-\-swap\fP \fIfrom-setname\fP \fIto-setname\fP
-Swap the content of two sets, or in another words,
-exchange the name of two sets. The referred sets must exist and
-identical type of sets can be swapped only.
-.TP
-\fB\-L\fP, \fB\-\-list\fP [\fIsetname\fP]
-List the entries for the specified set, or for
+If the set has got reference(s), nothing is done and no set destroyed.
+.TP
+\fBlist\fP [ \fISETNAME\fP ]
+List the header data and the entries for the specified set, or for
all sets if none is given. The
-\fB\-r\fP/\fB\-\-resolve\fP
+\fB\-\-resolve\fP
option can be used to force name lookups (which may be slow). When the
-\fB\-s\fP/\fB\-\-sorted\fP
+\fB\-\-sorted\fP
option is given, the entries are listed sorted (if the given set
-type supports the operation).
-.TP
-\fB\-S\fP, \fB\-\-save\fP [\fIsetname\fP]
+type supports the operation). The option
+\fB\-\-output\fR
+can be used to control the format of the listing:
+\fBplain\fR, \fBsave\fR or \fBxml\fR.
+The default is
+\fBplain\fR.
+.TP
+\fBsave\fP [ \fISETNAME\fP ]
Save the given set, or all sets if none is given
-to stdout in a format that \fB\-\-restore\fP can read.
-.TP
-\fB\-R\fP, \fB\-\-restore\fP
-Restore a saved session generated by \fB\-\-save\fP. The saved session
-can be fed from stdin.
-
-When generating a session file please note that the supported commands
-(create set and add element) must appear in a strict order: first create
-the set, then add all elements. Then create the next set, add all its elements
-and so on. Also, it is a restore operation, so the sets being restored must
-not exist.
-.TP
-\fB\-A\fP, \fB\-\-add\fP \fIsetname\fP \fIentry\fP
-Add an entry to a set.
-.TP
-\fB\-D\fP, \fB\-\-del\fP \fIsetname\fP \fIentry\fP
-Delete an entry from a set.
-.TP
-\fB-T\fP, \fB\-\-test\fP \fIsetname\fP \fIentry\fP
-Test wether an entry is in a set or not. Exit status number is zero
-if the tested entry is in the set and nonzero if it is missing from
-the set.
-.TP
-\fB\-H\fP, \fB\-\-help\fP [\fIsettype\fP]
-Print help and settype specific help if settype specified.
-.TP
-\fB\-V\fP, \fB\-v\fP, \fB\-\-version\fP
-Print program version and protocol version.
+to stdout in a format that
+\fBrestore\fP
+can read.
+.TP
+\fBrestore\fP
+Restore a saved session generated by
+\fBsave\fP.
+The saved session can be fed from stdin.
+.TP
+\fBflush\fP [ \fISETNAME\fP ]
+Flush all entries from the specified set or flush
+all sets if none is given.
+.TP
+\fBe\fP, \fBrename\fP \fISETNAME\-FROM\fP \fISETNAME\-TO\fP
+Rename a set. Set identified by
+\fISETNAME\-TO\fR
+must not exist.
+.TP
+\fBw\fP, \fBswap\fP \fISETNAME\-FROM\fP \fISETNAME\-TO\fP
+Swap the content of two sets, or in another words,
+exchange the name of two sets. The referred sets must exist and
+identical type of sets can be swapped only.
+.TP
+\fBhelp\fP [ \fITYPENAME\fP ]
+Print help and set type specific help if
+\fITYPENAME\fR
+is specified.
+.TP
+\fBversion\fP
+Print program version.
+.TP
+\fB\-\fP
+If a dash is specified as command, then
+\fBipset\fR
+enters a simple interactive mode and the commands are read from the standard input.
+The interactive mode can be finished by entering the pseudo\-command
+\fBquit\fR.
.P
.SS "OTHER OPTIONS"
-The following additional options can be specified:
-.TP
-\fB\-r\fP, \fB\-\-resolve\fP
+The following additional options can be specified. The long option names
+cannot be abbreviated.
+.TP
+\fB\-!\fP, \fB\-exist\fP
+Ignore errors when the exactly the same set is to be created or already
+added entry is added or missing entry is deleted.
+.TP
+\fB\-o\fP, \fB\-output\fP { \fBplain\fR | \fBsave\fR | \fBxml\fR }
+Select the output format to the
+\fBlist\fR
+command.
+.TP
+\fB\-q\fP, \fB\-quiet\fP
+Suppress any output to stdout and stderr.
+\fBipset\fR
+will still exit with error if it cannot continue.
+.TP
+\fB\-r\fP, \fB\-resolve\fP
When listing sets, enforce name lookup. The
program will try to display the IP entries resolved to
-host names or services (whenever applicable), which can trigger
-.B
-slow
-DNS
-lookups.
-.TP
-\fB\-s\fP, \fB\-\-sorted\fP
+host names which requires
+\fBslow\fR
+DNS lookups.
+.TP
+\fB\-s\fP, \fB\-sorted\fP
Sorted output. When listing sets, entries are listed sorted.
-.TP
-\fB\-n\fP, \fB\-\-numeric\fP
-Numeric output. When listing sets, IP addresses and
-port numbers will be printed in numeric format. This is the default.
-.TP
-\fB\-q\fP, \fB\-\-quiet\fP
-Suppress any output to stdout and stderr. ipset will still return
-possible errors.
-.SH SET TYPES
-ipset supports the following set types:
-.SS ipmap
-The ipmap set type uses a memory range, where each bit represents
-one IP address. An ipmap set can store up to 65536 (B-class network)
-IP addresses. The ipmap set type is very fast and memory cheap, great
-for use when one want to match certain IPs in a range. If the optional
-\fB\-\-netmask\fP
-parameter is specified with a CIDR netmask value between 1-31 then
-network addresses are stored in the given set: i.e an
-IP address will be in the set if the network address, which is resulted
-by masking the address with the specified netmask, can be found in the set.
-.P
-Options to use when creating an ipmap set:
-.TP
-\fB\-\-from\fP \fIfrom-addr\fP
-.TP
-\fB\-\-to\fP \fIto-addr\fP
-Create an ipmap set from the specified address range.
-.TP
-\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP
-Create an ipmap set from the specified network.
-.TP
-\fB\-\-netmask\fP \fIprefixlen\fP
-When the optional
-\fB\-\-netmask\fP
-parameter specified, network addresses will be
-stored in the set instead of IP addresses, and the \fIfrom-addr\fP parameter
-must be a network address. The \fIprefixlen\fP value must be between 1-31.
-.PP
-Example:
-.IP
-ipset \-N test ipmap \-\-network 192.168.0.0/16
-.SS macipmap
-The macipmap set type uses a memory range, where each 8 bytes
-represents one IP and a MAC addresses. A macipmap set type can store
-up to 65536 (B-class network) IP addresses with MAC.
-When adding an entry to a macipmap set, you must specify the entry as
-"\fIaddress\fP\fB,\fP\fImac\fP".
-When deleting or testing macipmap entries, the
-"\fB,\fP\fImac\fP"
-part is not mandatory.
-.P
-Options to use when creating an macipmap set:
-.TP
-\fB\-\-from\fP \fIfrom-addr\fP
-.TP
-\fB\-\-to\fP \fIto-addr\fP
-Create a macipmap set from the specified address range.
-.TP
-\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP
-Create a macipmap set from the specified network.
-.TP
-\fB\-\-matchunset\fP
-When the optional
-\fB\-\-matchunset\fP
-parameter specified, IP addresses which could be stored
-in the set but not set yet, will always match.
-.P
-Please note, the
-"set"
-and
-"SET"
-netfilter kernel modules
-.B
-always
-use the source MAC address from the packet to match, add or delete
-entries from a macipmap type of set.
-.SS portmap
-The portmap set type uses a memory range, where each bit represents
-one port. A portmap set type can store up to 65536 ports.
-The portmap set type is very fast and memory cheap.
-.P
-Options to use when creating an portmap set:
-.TP
-\fB\-\-from\fP \fIfrom-port\fP
-.TP
-\fB\-\-to\fP \fIto-port\fP
-Create a portmap set from the specified port range.
-.SS iphash
-The iphash set type uses a hash to store IP addresses.
-In order to avoid clashes in the hash double-hashing, and as a last
-resort, dynamic growing of the hash performed. The iphash set type is
-great to store random addresses. If the optional
-\fB\-\-netmask\fP
-parameter is specified with a CIDR prefix length value between 1-31 then
-network addresses are stored in the given set: i.e an
-IP address will be in the set if the network address, which is resulted
-by masking the address with the specified netmask, can be found in the set.
-.P
-Options to use when creating an iphash set:
-.TP
-\fB\-\-hashsize\fP \fIhashsize\fP
-The initial hash size (default 1024)
-.TP
-\fB\-\-probes\fP \fIprobes\fP
-How many times try to resolve clashing at adding an IP to the hash
-by double-hashing (default 8).
-.TP
-\fB\-\-resize\fP \fIpercent\fP
-Increase the hash size by this many percent (default 50) when adding
-an IP to the hash could not be performed after
-\fIprobes\fP
-number of double-hashing.
-.TP
-\fB\-\-netmask\fP \fIprefixlen\fP
-When the optional
-\fB\-\-netmask\fP
-parameter specified, network addresses will be
-stored in the set instead of IP addresses. The \fIprefixlen\fP value must
-be between 1-31.
-.P
-The iphash type of sets can store up to 65536 entries. If a set is full,
-no new entries can be added to it.
-.P
-Sets created by zero valued resize parameter won't be resized at all.
-The lookup time in an iphash type of set grows approximately linearly with
-the value of the
-\fIprobes\fP
-parameter. In general higher
-\fIprobes\fP
-value results better utilized hash while smaller value
-produces larger, sparser hash.
-.PP
-Example:
-.IP
-ipset \-N test iphash \-\-probes 2
-.SS nethash
-The nethash set type uses a hash to store different size of
-network addresses. The
-.I
-entry
-used in the ipset commands must be in the form
-"\fIaddress\fP\fB/\fP\fIprefixlen\fP"
-where prefixlen must be in the inclusive range of 1-31.
-In order to avoid clashes in the hash
-double-hashing, and as a last resort, dynamic growing of the hash performed.
-.P
-Options to use when creating an nethash set:
-.TP
-\fB\-\-hashsize\fP \fIhashsize\fP
-The initial hash size (default 1024)
-.TP
-\fB\-\-probes\fP \fIprobes\fP
-How many times try to resolve clashing at adding an IP to the hash
-by double-hashing (default 4).
-.TP
-\fB\-\-resize\fP \fIpercent\fP
-Increase the hash size by this many percent (default 50) when adding
-an IP to the hash could not be performed after
-.P
-The nethash type of sets can store up to 65536 entries. If a set is full,
-no new entries can be added to it.
-.P
-An IP address will be in a nethash type of set if it belongs to any of the
-netblocks added to the set. The matching always start from the smallest
-size of netblock (most specific netmask) to the largest ones (least
-specific netmasks). When adding/deleting IP addresses
-to a nethash set by the
-"SET"
-netfilter kernel module, it will be added/deleted by the smallest
-netblock size which can be found in the set, or by /31 if the set is empty.
-.P
-The lookup time in a nethash type of set grows approximately linearly
-with the times of the
-\fIprobes\fP
-parameter and the number of different mask parameters in the hash.
-Otherwise the same speed and memory efficiency comments applies here
-as at the iphash type.
-.SS ipporthash
-The ipporthash set type uses a hash to store IP address and port pairs.
-In order to avoid clashes in the hash double-hashing, and as a last
-resort, dynamic growing of the hash performed. An ipporthash set can
-store up to 65536 (B-class network) IP addresses with all possible port
-values. When adding, deleting and testing values in an ipporthash type of
-set, the entries must be specified as
-"\fIaddress\fP\fB,\fP\fIport\fP".
-.P
-The ipporthash types of sets evaluates two src/dst parameters of the
-"set"
-match and
-"SET"
-target.
-.P
-Options to use when creating an ipporthash set:
-.TP
-\fB\-\-from\fP \fIfrom-addr\fP
-.TP
-\fB\-\-to\fP \fIto-addr\fP
-Create an ipporthash set from the specified address range.
-.TP
-\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP
-Create an ipporthash set from the specified network.
-.TP
-\fB\-\-hashsize\fP \fIhashsize\fP
-The initial hash size (default 1024)
-.TP
-\fB\-\-probes\fP \fIprobes\fP
-How many times try to resolve clashing at adding an IP to the hash
-by double-hashing (default 8).
-.TP
-\fB\-\-resize\fP \fIpercent\fP
-Increase the hash size by this many percent (default 50) when adding
-an IP to the hash could not be performed after
-\fIprobes\fP
-number of double-hashing.
-.P
-The same resizing, speed and memory efficiency comments applies here
-as at the iphash type.
-.SS ipportiphash
-The ipportiphash set type uses a hash to store IP address,port and IP
-address triples. The first IP address must come form a maximum /16
-sized network or range while the port number and the second IP address
-parameters are arbitrary. When adding, deleting and testing values in an
-ipportiphash type of set, the entries must be specified as
-"\fIaddress\fP\fB,\fP\fIport\fP\fB,\fP\fIaddress\fP".
-.P
-The ipportiphash types of sets evaluates three src/dst parameters of the
-"set"
-match and
-"SET"
-target.
-.P
-Options to use when creating an ipportiphash set:
-.TP
-\fB\-\-from\fP \fIfrom-addr\fP
-.TP
-\fB\-\-to\fP \fIto-addr\fP
-Create an ipportiphash set from the specified address range.
-.TP
-\fB\-\-network\fP \fIaddr\fP\fB/\fP\fImask\fP
-Create an ipportiphash set from the specified network.
-.TP
-\fB\-\-hashsize\fP \fIhashsize\fP
-The initial hash size (default 1024)
-.TP
-\fB\-\-probes\fP \fIprobes\fP
-How many times try to resolve clashing at adding an IP to the hash
-by double-hashing (default 8).
-.TP
-\fB\-\-resize\fP \fIpercent\fP
-Increase the hash size by this many percent (default 50) when adding
-an IP to the hash could not be performed after
-\fIprobes\fP
-number of double-hashing.
-.P
-The same resizing, speed and memory efficiency comments applies here
-as at the iphash type.
-.SS ipportnethash
-The ipportnethash set type uses a hash to store IP address, port, and
-network address triples. The IP address must come form a maximum /16
-sized network or range while the port number and the network address
-parameters are arbitrary, but the size of the network address must be
-between /1-/31. When adding, deleting
-and testing values in an ipportnethash type of set, the entries must be
-specified as
-"\fIaddress\fP\fB,\fP\fIport\fP\fB,\fP\fIaddress\fP\fB/\fP\fIprefixlen\fP".
-.P
-The ipportnethash types of sets evaluates three src/dst parameters of the
-"set"
-match and
-"SET"
-target.
-.P
-Options to use when creating an ipportnethash set:
-.TP
-\fB\-\-from\fP \fIfrom-address\fP
-.TP
-\fB\-\-to\fP \fIto-address\fP
-Create an ipporthash set from the specified range.
-.TP
-\fB\-\-network\fP \fIaddress\fP\fB/\fP\fImask\fP
-Create an ipporthash set from the specified network.
-.TP
-\fB\-\-hashsize\fP \fIhashsize\fP
-The initial hash size (default 1024)
-.TP
-\fB\-\-probes\fP \fIprobes\fP
-How many times try to resolve clashing at adding an IP to the hash
-by double-hashing (default 8).
-.TP
-\fB\-\-resize\fP \fIpercent\fP
-Increase the hash size by this many percent (default 50) when adding
-an IP to the hash could not be performed after
-\fIprobes\fP
-number of double-hashing.
-.P
-The same resizing, speed and memory efficiency comments applies here
-as at the iphash type.
-.SS iptree
-The iptree set type uses a tree to store IP addresses, optionally
-with timeout values.
-.P
-Options to use when creating an iptree set:
-.TP
-\fB\-\-timeout\fP \fIvalue\fP
-The timeout value for the entries in seconds (default 0)
-.P
-If a set was created with a nonzero valued
-\fB\-\-timeout\fP
-parameter then one may add IP addresses to the set with a specific
-timeout value using the syntax
-"\fIaddress\fP\fB,\fP\fItimeout-value\fP".
-Similarly to the hash types, the iptree type of sets can store up to 65536
-entries.
-.SS iptreemap
-The iptreemap set type uses a tree to store IP addresses or networks,
-where the last octet of an IP address are stored in a bitmap.
-As input entry, you can add IP addresses, CIDR blocks or network ranges
-to the set. Network ranges can be specified in the format
-"\fIaddress1\fP\fB-\fP\fIaddress2\fP".
-.P
-Options to use when creating an iptreemap set:
-.TP
-\fB\-\-gc\fP \fIvalue\fP
-How often the garbage collection should be called, in seconds (default 300)
-.SS setlist
-The setlist type uses a simple list in which you can store sets. By the
-ipset
-command you can add, delete and test sets in a setlist type of set.
-You can specify the sets as
-"\fIsetname\fP[\fB,\fP{\fBafter\fP|\fBbefore\fP},\fIsetname\fP]".
-By default new sets are added after (appended to) the existing
-elements. Setlist type of sets cannot be added to a setlist type of set.
-.P
-Options to use when creating a setlist type of set:
-.TP
-\fB\-\-size\fP \fIsize\fP
-Create a setlist type of set with the given size (default 8).
-.PP
-By the
-"set"
-match or
-"SET"
-target of
-\fBiptables\fP(8)
-you can test, add or delete entries in the sets. The match
-will try to find a matching IP address/port in the sets and
-the target will try to add the IP address/port to the first set
-to which it can be added. The number of src,dst options of
-the match and target are important: sets which eats more src,dst
-parameters than specified are skipped, while sets with equal
-or less parameters are checked, elements added. For example
-if
-.I
-a
-and
-.I
-b
-are setlist type of sets then in the command
-.IP
-iptables \-m set \-\-match\-set a src,dst \-j SET \-\-add-set b src,dst
-.PP
-the match and target will skip any set in
-.I a
-and
-.I b
-which stores
-data triples, but will check all sets with single or double
-data storage in
-.I a
+.SH "SET TYPES"
+A set type comprises of the storage method by which the data is stored and
+the data type(s) which are stored in the set. Therefore the
+\fITYPENAME\fR
+parameter of the
+\fBcreate\fR
+command follows the syntax
+
+\fITYPENAME\fR := \fImethod\fR\fB:\fR\fItype\fR[\fB,\fR\fItype\fR[\fB,\fR\fItype\fR]]
+
+where the current list of the methods are
+\fBbitmap\fR, \fBhash\fR, \fBlist\fR and the possible data types are \fBip\fR,
+\fBmac\fR and \fBport\fR.
+
+When adding, deleting or testing entries in a set, the same comma separated
+data syntax must be used for the entry parameter of the commands, i.e
+
+ipset add foo ipaddr,portnum,ipaddr
+
+All set types support the optional
+
+\fBtimeout\fR \fIvalue\fR
+
+parameter when creating a set and adding entries. The value of the \fBtimeout\fR
+parameter for the \fBcreate\fR command means the default timeout value (in seconds)
+for new entries. If a set is created with timeout support, then the same
+\fBtimeout\fR option can be used to specify non\-default timeout values
+when adding entries. Zero timeout value means the entry is added permanent to the set.
+.SS bitmap:ip
+The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host
+(default) or IPv4 network addresses. A \fBbitmap:ip\fR type of set can store up
+to 65536 entries.
+.PP
+\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR }
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR }
+.PP
+\fITEST\-ENTRY\fR := { \fIipaddr\fR }
+.PP
+Mandatory \fBcreate\fR options:
+.TP
+\fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR
+Create the set from the specified inclusive address range expressed in an
+IPv4 address range or network. The size of the range (in entries) cannot exceed
+the limit of maximum 65536 elements.
+.PP
+Optional \fBcreate\fR options:
+.TP
+\fBnetmask\fP \fIcidr\fP
+When the optional \fBnetmask\fP parameter specified, network addresses will be
+stored in the set instead of IP host addresses. The \fIcidr\fR value must be
+between 1\-32.
+An IP address will be in the set if the network address, which is resulted by
+masking the address with the specified netmask calculated from the cidr value,
+can be found in the set.
+.PP
+Examples:
+.IP
+ipset create foo bitmap:ip range 192.168.0.0/16
+.IP
+ipset add foo 192.168.1/24
+.IP
+ipset test foo 192.168.1.1
+.SS bitmap:ip,mac
+The \fBbitmap:ip,mac\fR set type uses a memory range to store IPv4 and a MAC address pairs. A \fBbitmap:ip,mac\fR type of set can store up to 65536 entries.
+.PP
+\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] }
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] }
+.PP
+\fITEST\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] }
+.PP
+Mandatory options to use when creating a \fBbitmap:ip,mac\fR type of set:
+.TP
+\fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR
+Create the set from the specified inclusive address range expressed in an
+IPv4 address range or network. The size of the range cannot exceed the limit
+of maximum 65536 entries.
+.PP
+The \fBbitmap:ip,mac\fR type is exceptional in the sense that the MAC part can
+be left out when adding/deleting/testing entries in the set. If
+we add an entry without the MAC address specified, when the first time the entry is
+matched by the kernel, it will automatically fill out the missing part with the
+source MAC address from the packet. If the entry was specified with a timeout value,
+the timer starts off when the IP and MAC address pair is complete.
+.PP
+Please note, the \fBset\fR match and \fBSET\fR target netfilter kernel modules
+\fBalways\fR use the source MAC address from the packet to match, add or delete
+entries from a \fBbitmap:ip,mac\fR type of set.
+.PP
+Examples:
+.IP
+ipset create foo bitmap:ip,mac range 192.168.0.0/16
+.IP
+ipset add foo 192.168.1,12:34:56:78:9A:BC
+.IP
+ipset test foo 192.168.1.1
+.SS bitmap:port
+The \fBbitmap:port\fR set type uses a memory range to store port numbers
+and such a set can store up to 65536 ports.
+.PP
+\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-port\fP\-\fIto\-port [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := { \fIport\fR | \fIfrom\-port\fR\-\fIto\-port\fR }
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := {\fIport\fR | \fIfrom\-port\fR\-\fIto\-port\fR }
+.PP
+\fITEST\-ENTRY\fR := { \fIport\fR }
+.PP
+Mandatory options to use when creating a \fBbitmap:port\fR type of set:
+.TP
+\fBrange\fP \fIfrom\-port\fP\-\fIto\-port\fR
+Create the set from the specified inclusive port range.
+.PP
+Examples:
+.IP
+ipset create foo bitmap:port range 0\-1024
+.IP
+ipset add foo 80
+.IP
+ipset test foo 80
+.SS hash:ip
+The \fBhash:ip\fR set type uses a hash to store IP addresses.
+In order to avoid clashes in the hash a limited number of chaining, and then
+if that is exhausted, the doubling of the hash is performed.
+.PP
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := { \fIipaddr\fR }
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR }
+.PP
+\fITEST\-ENTRY\fR := { \fIipaddr\fR }
+.PP
+For the \fBinet\fR family one can add or delete multiple entries by specifying
+a range or a network:
+.PP
+\fIADD\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR }
+.PP
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR }
+.PP
+Optional \fBcreate\fR options:
+.TP
+\fBfamily\fR { \fBinet\fR|\fBinet6\fR }
+The protocol family of the IP addresses to be stored in the set. The default is
+\fBinet\fR, i.e IPv4.
+.TP
+\fBhashsize\fR \fIvalue\fR
+The initial hash size for the set, default is 1024. The hash size must be a power
+of two, the kernel automatically rounds up non power of two hash sizes to the first
+correct value.
+.TP
+\fBmaxelem\fR \fIvalue\fR
+The maximal number of elements which can be stored in the set, default 65536.
+.TP
+\fBnetmask\fP \fIcidr\fP
+When the optional \fBnetmask\fP parameter specified, network addresses will be
+stored in the set instead of IP host addresses. The \fIcidr\fP value must be
+between 1\-32 for IPv4 and between 1\-128 for IPv6. An IP address will be in the set
+if the network address, which is resulted by masking the address with the netmask
+calculated from the cidr, can be found in the set.
+.PP
+Examples:
+.IP
+ipset create foo hash:ip netmask 24
+.IP
+ipset add foo 192.168.1.1
+.IP
+ipset test foo 192.168.1.2
+.SS hash:net
+The \fBhash:net\fR set type uses a hash to store different sized of IP networks.
+In order to avoid clashes in the hash a limited number of chaining, and then
+if that is exhausted, the doubling of the hash is performed.
+.PP
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] }
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] }
+.PP
+\fITEST\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] }
+.PP
+Optional \fBcreate\fR options:
+.TP
+\fBfamily\fR { \fBinet\fR|\fBinet6\fR }
+The protocol family of the IP addresses to be stored in the set. The default is
+\fBinet\fR, i.e IPv4.
+.TP
+\fBhashsize\fR \fIvalue\fR
+The initial hash size for the set, default is 1024. The hash size must be a power
+of two, the kernel automatically rounds up non power of two hash sizes to the first
+correct value.
+.TP
+\fBmaxelem\fR \fIvalue\fR
+The maximal number of elements which can be stored in the set, default 65536.
+.PP
+When adding/deleting/testing entries, if the cidr parameter is not specified,
+then the host cidr value is assumed.
+.PP
+From the \fBset\fR netfilter match point of view an IP address will be in a \fBhash:net\fR type of set if it belongs to any of the netblocks added to the set.
+The matching always start from the smallest size of netblock (most specific
+cidr) to the largest ones (least specific cidr). When adding/deleting IP
+addresses to the set by the \fBSET\fR netfilter target, it will be
+added/deleted by the most specific cidr which can be found in the
+set, or by the host cidr value if the set is empty.
+.PP
+The lookup time grows linearly with the number of the different \fIcidr\fR
+values added to the set.
+.PP
+Examples:
+.IP
+ipset create foo hash:net
+.IP
+ipset add foo 192.168.0/24
+.IP
+ipset add foo 10.1.0.0/16
+.IP
+ipset test foo 192.168.0/24
+.SS hash:ip,port
+The \fBhash:ip,port\fR set type uses a hash to store IP address and port pairs.
+In order to avoid clashes in the hash a limited number of chaining, and then
+if that is exhausted, the doubling of the hash is performed.
+.PP
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR }
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR }
+.PP
+\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR }
+.PP
+Optional \fBcreate\fR options:
+.TP
+\fBfamily\fR { \fBinet\fR|\fBinet6\fR }
+The protocol family of the IP addresses to be stored in the set. The default is
+\fBinet\fR, i.e IPv4.
+.TP
+\fBhashsize\fR \fIvalue\fR
+The initial hash size for the set, default is 1024. The hash size must be a power
+of two, the kernel automatically rounds up non power of two hash sizes to the first
+correct value.
+.TP
+\fBmaxelem\fR \fIvalue\fR
+The maximal number of elements which can be stored in the set, default 65536.
+.PP
+The \fBhash:ip,port\fR type of sets require two \fBsrc\fR/\fBdst\fR parameters of
+the \fBset\fR match and \fBSET\fR target kernel modules.
+.PP
+Examples:
+.IP
+ipset create foo hash:ip,port
+.IP
+ipset add foo 192.168.1.1,80
+.IP
+ipset test foo 192.168.1.1,80
+.SS hash:ip,port,ip
+The \fBhash:ip,port,ip\fR set type uses a hash to store IP address, port and
+IP address triples. In order to avoid clashes in the hash a limited number of
+chaining, and then if that is exhausted, the doubling of the hash is performed.
+.PP
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR }
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR }
+.PP
+\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR }
+.PP
+Optional \fBcreate\fR options:
+.TP
+\fBfamily\fR { \fBinet\fR|\fBinet6\fR }
+The protocol family of the IP addresses to be stored in the set. The default is
+\fBinet\fR, i.e IPv4.
+.TP
+\fBhashsize\fR \fIvalue\fR
+The initial hash size for the set, default is 1024. The hash size must be a power
+of two, the kernel automatically rounds up non power of two hash sizes to the first
+correct value.
+.TP
+\fBmaxelem\fR \fIvalue\fR
+The maximal number of elements which can be stored in the set, default 65536.
+.PP
+The \fBhash:ip,port,ip\fR type of sets require three \fBsrc\fR/\fBdst\fR parameters of
+the \fBset\fR match and \fBSET\fR target kernel modules.
+.PP
+Examples:
+.IP
+ipset create foo hash:ip,port,ip
+.IP
+ipset add foo 192.168.1.1,80,10.0.0.1
+.IP
+ipset test foo 192.168.1.1,80,10.0.0.1
+.SS hash:ip,port,net
+The \fBhash:ip,port,net\fR set type uses a hash to store IP address, port and
+IP network triples.
+In order to avoid clashes in the hash a limited number of chaining, and then
+if that is exhausted, the doubling of the hash is performed.
+.PP
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] }
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] }
+.PP
+\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] }
+.PP
+Optional \fBcreate\fR options:
+.TP
+\fBfamily\fR { \fBinet\fR|\fBinet6\fR }
+The protocol family of the IP addresses to be stored in the set. The default is
+\fBinet\fR, i.e IPv4.
+.TP
+\fBhashsize\fR \fIvalue\fR
+The initial hash size for the set, default is 1024. The hash size must be a power
+of two, the kernel automatically rounds up non power of two hash sizes to the first
+correct value.
+.TP
+\fBmaxelem\fR \fIvalue\fR
+The maximal number of elements which can be stored in the set, default 65536.
+.PP
+When adding/deleting/testing entries, if the cidr parameter is not specified,
+then the host cidr value is assumed.
+.PP
+From the \fBset\fR netfilter match point of view a triple will be in a \fBhash:ip,port,net\fR type of set (when the first IP and the port match)
+if the second IP belongs to any of the netblocks added to the set.
+The matching always start from the smallest size of netblock (most specific
+cidr) to the largest ones (least specific cidr). When adding/deleting triples
+to the set by the \fBSET\fR netfilter target, it will be
+added/deleted by the most specific cidr which can be found in the
+set, or by the host cidr value if the set is empty.
+.PP
+The lookup time grows linearly with the number of the different \fIcidr\fR
+values added to the set.
+.PP
+The \fBhash:ip,port,net\fR type of sets require three \fBsrc\fR/\fBdst\fR parameters of
+the \fBset\fR match and \fBSET\fR target kernel modules.
+.PP
+Examples:
+.IP
+ipset create foo hash:ip,port,net
+.IP
+ipset add foo 192.168.1,80,10.0.0/24
+.IP
+ipset add foo 192.168.2,25,10.1.0.0/16
+.IP
+ipset test foo 192.168.1,80.10.0.0/24
+.SS list:set
+The \fBlist:set\fR type uses a simple list in which you can store
+sets.
+.PP
+\fICREATE\-OPTIONS\fR := [ \fBsize\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIADD\-ENTRY\fR := \fIsetname\fR [ \fBbefore\fR|\fBafter\fR \fIsetname\fR ]
+.PP
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+.PP
+\fIDEL\-ENTRY\fR := \fIsetname\fR [ \fBbefore\fR|\fBafter\fR \fIsetname\fR ]
+.PP
+\fITEST\-ENTRY\fR := \fIsetname\fR [ \fBbefore\fR|\fBafter\fR \fIsetname\fR ]
+.PP
+Optional \fBcreate\fR options:
+.TP
+\fBsize\fR \fIvalue\fR
+The size of the list, the default is 8.
+.PP
+By the \fBipset\fR commad you can add, delete and test sets in a
+\fBlist:set\fR type of set.
+.PP
+By the \fBset\fR match or \fBSET\fR target of netfiler
+you can test, add or delete entries in the sets added to the \fBlist:set\fR
+type of set. The match will try to find a matching entry in the sets and
+the target will try to add an entry to the first set to which it can be added.
+The number of src,dst options of the match and target are important: sets which
+eats more src,dst parameters than specified are skipped, while sets with equal
+or less parameters are checked, elements added. For example if \fIa\fR and
+\fIb\fR are \fBlist:set\fR type of sets then in the command
+.IP
+iptables \-m set \-\-match\-set a src,dst \-j SET \-\-add\-set b src,dst
+.PP
+the match and target will skip any set in \fIa\fR and \fIb\fR
+which stores data triples, but will check all sets with single or double
+data storage in \fIa\fR
set and add src to the first single or src,dst to the first double
-data storage set in
-\fIb\fP.
-.P
+data storage set in \fIb\fR.
+.PP
You can imagine a setlist type of set as an ordered union of
the set elements.
-.SH GENERAL RESTRICTIONS
-Setnames starting with colon (:) cannot be defined. Zero valued set
-entries cannot be used with hash type of sets.
-.SH COMMENTS
+.SH "GENERAL RESTRICTIONS"
+Zero valued set entries cannot be used with hash methods.
+.SH "COMMENTS"
If you want to store same size subnets from a given network
-(say /24 blocks from a /8 network), use the ipmap set type.
+(say /24 blocks from a /8 network), use the \fBbitmap:ip\fR set type.
If you want to store random same size networks (say random /24 blocks),
-use the iphash set type. If you have got random size of netblocks,
-use nethash.
-.P
-Old separator tokens (':' and '%") are still accepted.
-.P
-Binding support is removed.
-.SH DIAGNOSTICS
+use the \fBhash:ip\fR set type. If you have got random size of netblocks,
+use \fBhash:net\fR.
+.PP
+Backward compatibility is maintained and old \fBipset\fR syntax is still supported.
+.PP
+The \fBiptree\fR and \fBiptreemap\fR set types are removed: if you refer to them,
+they are automatically replaced by \fBhash:ip\fR type of sets.
+.SH "DIAGNOSTICS"
Various error messages are printed to standard error. The exit code
-is 0 for correct functioning. Errors which appear to be caused by
-invalid or abused command line parameters cause an exit code of 2, and
-other errors cause an exit code of 1.
-.SH BUGS
-Bugs? No, just funny features. :-)
+is 0 for correct functioning.
+.SH "BUGS"
+Bugs? No, just funny features. :\-)
OK, just kidding...
-.SH SEE ALSO
-.BR iptables (8),
-.SH AUTHORS
+.SH "SEE ALSO"
+\fBiptables\fR(8),
+\fBip6tables\fR(8)
+.SH "AUTHORS"
Jozsef Kadlecsik wrote ipset, which is based on ippool by
Joakim Axelsson, Patrick Schaaf and Martin Josefsson.
-.P
+.br
Sven Wegener wrote the iptreemap type.
-.SH LAST REMARK
-.BR "I stand on the shoulders of giants."
+.SH "LAST REMARK"
+\fBI stand on the shoulders of giants.\fR
diff --git a/src/ipset.c b/src/ipset.c
index d29042d..69fcd09 100644
--- a/src/ipset.c
+++ b/src/ipset.c
@@ -15,11 +15,12 @@
#include <config.h>
+#include <libipset/debug.h> /* D() */
#include <libipset/parse.h> /* ipset_parse_* */
#include <libipset/session.h> /* ipset_session_* */
#include <libipset/types.h> /* struct ipset_type */
#include <libipset/ui.h> /* core options, commands */
-#include <libipset/utils.h> /* ipset_name_match */
+#include <libipset/utils.h> /* STREQ */
static char program_name[] = PACKAGE;
static char program_version[] = PACKAGE_VERSION;
@@ -31,6 +32,18 @@ static char cmdline[1024];
static char *newargv[255];
static int newargc = 0;
+/* The known set types: (typename, revision, family) is unique */
+extern struct ipset_type ipset_bitmap_ip0;
+extern struct ipset_type ipset_bitmap_ipmac0;
+extern struct ipset_type ipset_bitmap_port0;
+extern struct ipset_type ipset_hash_ip0;
+extern struct ipset_type ipset_hash_net0;
+extern struct ipset_type ipset_hash_ipport0;
+extern struct ipset_type ipset_hash_ipportip0;
+extern struct ipset_type ipset_hash_ipportnet0;
+extern struct ipset_type ipset_tree_ip0;
+extern struct ipset_type ipset_list_set0;
+
enum exittype {
NO_PROBLEM = 0,
OTHER_PROBLEM,
@@ -38,7 +51,7 @@ enum exittype {
VERSION_PROBLEM,
};
-static void __attribute__((format(printf,2,3)))
+static int __attribute__((format(printf,2,3)))
exit_error(int status, const char *msg, ...)
{
bool quiet = !interactive
@@ -52,7 +65,8 @@ exit_error(int status, const char *msg, ...)
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
- fprintf(stderr, "\n");
+ if (msg[strlen(msg) - 1] != '\n')
+ fprintf(stderr, "\n");
if (status == PARAMETER_PROBLEM)
fprintf(stderr,
@@ -63,7 +77,7 @@ exit_error(int status, const char *msg, ...)
if (status && interactive) {
if (session)
ipset_session_report_reset(session);
- return;
+ return -1;
}
if (session)
@@ -71,6 +85,8 @@ exit_error(int status, const char *msg, ...)
D("status: %u", status);
exit(status);
+ /* Unreached */
+ return -1;
}
static int
@@ -81,8 +97,8 @@ handle_error(void)
fprintf(stderr, "Warning: %s\n",
ipset_session_warning(session));
if (ipset_session_error(session))
- exit_error(OTHER_PROBLEM, "%s",
- ipset_session_error(session));
+ return exit_error(OTHER_PROBLEM, "%s",
+ ipset_session_error(session));
if (!interactive) {
ipset_session_fini(session);
@@ -96,19 +112,15 @@ handle_error(void)
static void
help(void)
{
- enum ipset_cmd cmd;
+ const struct ipset_commands *c;
const struct ipset_envopts *opt = ipset_envopts;
printf("%s v%s\n\n"
"Usage: %s [options] COMMAND\n\nCommands:\n",
program_name, program_version, program_name);
- for (cmd = IPSET_CMD_NONE + 1; cmd < IPSET_CMD_MAX; cmd++) {
- if (!ipset_commands[cmd-1].name[0])
- continue;
- printf("%s %s\n",
- ipset_commands[cmd-1].name[0],
- ipset_commands[cmd-1].help);
+ for (c = ipset_commands; c->cmd; c++) {
+ printf("%s %s\n", c->name[0], c->help);
}
printf("\nOptions:\n");
@@ -136,9 +148,11 @@ build_argv(char *buffer)
while ((ptr = strtok(NULL, " \t\n")) != NULL) {
if ((newargc + 1) < (int)(sizeof(newargv)/sizeof(char *)))
newargv[newargc++] = ptr;
- else
+ else {
exit_error(PARAMETER_PROBLEM,
"Line is too long to parse.");
+ return;
+ }
}
}
@@ -188,57 +202,61 @@ restore(char *argv0)
}
static int
-call_parser(int argc, char *argv[], const struct ipset_arg *args)
+call_parser(int argc, char *argv[], const struct ipset_arg *args)
{
int i = 1, ret = 0;
const struct ipset_arg *arg;
+ const char *optstr;
- /* Currently CREATE and ADD may have got additional arguments */
+ /* Currently CREATE and ADT may have got additional arguments */
if (!args)
goto done;
for (arg = args; arg->opt; arg++) {
for (i = 1; i < argc; ) {
- D("argc: %u, i: %u", argc, i);
- if (!(ipset_name_match(argv[i], arg->name))) {
+ D("argc: %u, i: %u: %s vs %s", argc, i, argv[i], arg->name[0]);
+ if (!(ipset_match_option(argv[i], arg->name))) {
i++;
continue;
}
+ optstr = argv[i];
/* Shift off matched option */
D("match %s", arg->name[0]);
ipset_shift_argv(&argc, argv, i);
D("argc: %u, i: %u", argc, i);
switch (arg->has_arg) {
case IPSET_MANDATORY_ARG:
- if (i + 1 > argc) {
- exit_error(PARAMETER_PROBLEM,
- "Missing mandatory argument of option `%s'",
- arg->name[0]);
- return 1;
- }
+ if (i + 1 > argc)
+ return exit_error(PARAMETER_PROBLEM,
+ "Missing mandatory argument of option `%s'",
+ arg->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
if (i + 1 <= argc) {
- ret = arg->parse(session, arg->opt,
- argv[i]);
+ ret = ipset_call_parser(session,
+ arg->parse,
+ optstr, arg->opt,
+ argv[i]);
if (ret < 0)
return ret;
ipset_shift_argv(&argc, argv, i);
+ break;
}
- break;
+ /* Fall through */
default:
- ret = ipset_data_set(ipset_session_data(session),
- arg->opt, arg->name[0]);
+ ret = ipset_call_parser(session,
+ arg->parse,
+ optstr, arg->opt,
+ optstr);
if (ret < 0)
return ret;
}
}
}
done:
- if (i < argc) {
- exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'",
- argv[i]);
- return 1;
- }
+ if (i < argc)
+ return exit_error(PARAMETER_PROBLEM,
+ "Unknown argument: `%s'",
+ argv[i]);
return ret;
}
@@ -256,12 +274,20 @@ check_mandatory(const struct ipset_type *type, int cmd)
mandatory &= ~flags;
if (!mandatory)
return;
+ if (!arg) {
+ exit_error(OTHER_PROBLEM,
+ "There are missing mandatory flags but can't check them. "
+ "It's a bug, please report the problem.");
+ return;
+ }
for (; arg->opt; arg++)
- if (mandatory & IPSET_FLAG(arg->opt))
+ if (mandatory & IPSET_FLAG(arg->opt)) {
exit_error(PARAMETER_PROBLEM,
"Mandatory option `%s' is missing",
arg->name[0]);
+ return;
+ }
}
static const struct ipset_type *
@@ -270,7 +296,7 @@ type_find(const char *name)
const struct ipset_type *t = ipset_types();
while (t) {
- if (STREQ(t->name, name) || STREQ(t->alias, name))
+ if (ipset_match_typename(name, t))
return t;
t = t->next;
}
@@ -299,7 +325,7 @@ parse_commandline(int argc, char *argv[])
{
int ret = 0;
enum ipset_cmd cmd = IPSET_CMD_NONE;
- int i = 0, j;
+ int i;
char *arg0 = NULL, *arg1 = NULL, *c;
const struct ipset_envopts *opt;
const struct ipset_commands *command;
@@ -309,16 +335,16 @@ parse_commandline(int argc, char *argv[])
if (session == NULL) {
session = ipset_session_init(printf);
if (session == NULL)
- exit_error(OTHER_PROBLEM,
- "Cannot initialize ipset session, aborting.");
+ return exit_error(OTHER_PROBLEM,
+ "Cannot initialize ipset session, aborting.");
}
/* Commandline parsing, somewhat similar to that of 'ip' */
/* First: parse core options */
- for (opt = ipset_envopts; opt->flag ; opt++) {
+ for (opt = ipset_envopts; opt->flag; opt++) {
for (i = 1; i < argc; ) {
- if (!ipset_name_match(argv[i], opt->name)) {
+ if (!ipset_match_envopt(argv[i], opt->name)) {
i++;
continue;
}
@@ -327,9 +353,9 @@ parse_commandline(int argc, char *argv[])
switch (opt->has_arg) {
case IPSET_MANDATORY_ARG:
if (i + 1 > argc)
- exit_error(PARAMETER_PROBLEM,
- "Missing mandatory argument to option %s",
- opt->name[0]);
+ return exit_error(PARAMETER_PROBLEM,
+ "Missing mandatory argument to option %s",
+ opt->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
if (i + 1 <= argc) {
@@ -340,53 +366,49 @@ parse_commandline(int argc, char *argv[])
ipset_shift_argv(&argc, argv, i);
}
break;
- default:
- ret = opt->parse(session, opt->flag, argv[i]);
+ case IPSET_NO_ARG:
+ ret = opt->parse(session, opt->flag,
+ opt->name[0]);
if (ret < 0)
return handle_error();
break;
+ default:
+ break;
}
}
}
/* Second: parse command */
- for (j = IPSET_CMD_NONE + 1; j < IPSET_CMD_MAX; j++) {
- command = &ipset_commands[j - 1];
- if (!command->name[0])
- continue;
+ for (command = ipset_commands;
+ command->cmd && cmd == IPSET_CMD_NONE;
+ command++) {
for (i = 1; i < argc; ) {
- if (!ipset_name_match(argv[i], command->name)) {
+ if (!ipset_match_cmd(argv[1], command->name)) {
i++;
continue;
}
- if (cmd != IPSET_CMD_NONE)
- exit_error(PARAMETER_PROBLEM,
- "Commands `%s' and `%s'"
- "cannot be specified together.",
- ipset_commands[cmd - 1].name[0],
- command->name[0]);
if (restore_line != 0
- && (j == IPSET_CMD_RESTORE
- || j == IPSET_CMD_VERSION
- || j == IPSET_CMD_HELP))
- exit_error(PARAMETER_PROBLEM,
- "Command `%s' is invalid in restore mode.",
- command->name[0]);
- if (interactive && j == IPSET_CMD_RESTORE) {
- printf("Restore command ignored in interactive mode\n");
+ && (command->cmd == IPSET_CMD_RESTORE
+ || command->cmd == IPSET_CMD_VERSION
+ || command->cmd == IPSET_CMD_HELP))
+ return exit_error(PARAMETER_PROBLEM,
+ "Command `%s' is invalid in restore mode.",
+ command->name[0]);
+ if (interactive && command->cmd == IPSET_CMD_RESTORE) {
+ printf("Restore command ignored in interactive mode\n");
return 0;
}
/* Shift off matched command arg */
ipset_shift_argv(&argc, argv, i);
- cmd = j;
+ cmd = command->cmd;
switch (command->has_arg) {
case IPSET_MANDATORY_ARG:
case IPSET_MANDATORY_ARG2:
if (i + 1 > argc)
- exit_error(PARAMETER_PROBLEM,
- "Missing mandatory argument to command %s",
- command->name[0]);
+ return exit_error(PARAMETER_PROBLEM,
+ "Missing mandatory argument to command %s",
+ command->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
arg0 = argv[i];
@@ -399,13 +421,14 @@ parse_commandline(int argc, char *argv[])
}
if (command->has_arg == IPSET_MANDATORY_ARG2) {
if (i + 1 > argc)
- exit_error(PARAMETER_PROBLEM,
- "Missing second mandatory argument to command %s",
- command->name[0]);
+ return exit_error(PARAMETER_PROBLEM,
+ "Missing second mandatory argument to command %s",
+ command->name[0]);
arg1 = argv[i];
/* Shift off second arg */
ipset_shift_argv(&argc, argv, i);
}
+ break;
}
}
@@ -433,14 +456,14 @@ parse_commandline(int argc, char *argv[])
parse_commandline(newargc, newargv);
printf("%s> ", program_name);
}
- exit_error(NO_PROBLEM, NULL);
+ return exit_error(NO_PROBLEM, NULL);
}
- exit_error(PARAMETER_PROBLEM, "No command specified.");
+ return exit_error(PARAMETER_PROBLEM, "No command specified.");
case IPSET_CMD_VERSION:
printf("%s v%s.\n", program_name, program_version);
if (interactive)
return 0;
- exit_error(NO_PROBLEM, NULL);
+ return exit_error(NO_PROBLEM, NULL);
case IPSET_CMD_HELP:
help();
@@ -450,8 +473,8 @@ parse_commandline(int argc, char *argv[])
/* Type-specific help, without kernel checking */
type = type_find(arg0);
if (!type)
- exit_error(PARAMETER_PROBLEM,
- "Unknown settype: `%s'", arg0);
+ return exit_error(PARAMETER_PROBLEM,
+ "Unknown settype: `%s'", arg0);
printf("\n%s type specific options:\n\n%s",
type->name, type->usage);
if (type->family == AF_UNSPEC)
@@ -475,7 +498,9 @@ parse_commandline(int argc, char *argv[])
}
if (interactive)
return 0;
- exit_error(NO_PROBLEM, NULL);
+ return exit_error(NO_PROBLEM, NULL);
+ case IPSET_CMD_QUIT:
+ return exit_error(NO_PROBLEM, NULL);
default:
break;
}
@@ -525,7 +550,7 @@ parse_commandline(int argc, char *argv[])
ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
if (ret < 0)
return handle_error();
- ret = ipset_parse_name(session, IPSET_OPT_SETNAME2, arg1);
+ ret = ipset_parse_setname(session, IPSET_OPT_SETNAME2, arg1);
if (ret < 0)
return handle_error();
break;
@@ -582,5 +607,16 @@ parse_commandline(int argc, char *argv[])
int
main(int argc, char *argv[])
{
+ /* Register types */
+ ipset_type_add(&ipset_bitmap_ip0);
+ ipset_type_add(&ipset_bitmap_ipmac0);
+ ipset_type_add(&ipset_bitmap_port0);
+ ipset_type_add(&ipset_hash_ip0);
+ ipset_type_add(&ipset_hash_net0);
+ ipset_type_add(&ipset_hash_ipport0);
+ ipset_type_add(&ipset_hash_ipportip0);
+ ipset_type_add(&ipset_hash_ipportnet0);
+ ipset_type_add(&ipset_list_set0);
+
return parse_commandline(argc, argv);
}
diff --git a/src/ipset_bitmap_ip.c b/src/ipset_bitmap_ip.c
index 471cc96..e28432b 100644
--- a/src/ipset_bitmap_ip.c
+++ b/src/ipset_bitmap_ip.c
@@ -11,28 +11,28 @@
/* Parse commandline arguments */
static const struct ipset_arg bitmap_ip_create_args[] = {
- { .name = { "range", "--range", NULL },
+ { .name = { "range", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_netrange, .print = ipset_print_ip,
},
- { .name = { "netmask", "--netmask", NULL },
+ { .name = { "netmask", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NETMASK,
.parse = ipset_parse_netmask, .print = ipset_print_number,
},
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
- { .name = { "--from", NULL },
+ { .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_single_ip,
},
- { .name = { "--to", NULL },
+ { .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
.parse = ipset_parse_single_ip,
},
- { .name = { "--network", NULL },
+ { .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_net,
},
@@ -40,7 +40,7 @@ static const struct ipset_arg bitmap_ip_create_args[] = {
};
static const struct ipset_arg bitmap_ip_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
@@ -56,7 +56,7 @@ static const char bitmap_ip_usage[] =
struct ipset_type ipset_bitmap_ip0 = {
.name = "bitmap:ip",
- .alias = "ipmap",
+ .alias = { "ipmap", NULL },
.revision = 0,
.family = AF_INET,
.dimension = IPSET_DIM_ONE,
diff --git a/src/ipset_bitmap_ipmac.c b/src/ipset_bitmap_ipmac.c
index 4985fc4..382ebb5 100644
--- a/src/ipset_bitmap_ipmac.c
+++ b/src/ipset_bitmap_ipmac.c
@@ -11,24 +11,24 @@
/* Parse commandline arguments */
static const struct ipset_arg bitmap_ipmac_create_args[] = {
- { .name = { "range", "--range", NULL },
+ { .name = { "range", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_netrange, .print = ipset_print_ip,
},
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
- { .name = { "--from", NULL },
+ { .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_single_ip,
},
- { .name = { "--to", NULL },
+ { .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
.parse = ipset_parse_single_ip,
},
- { .name = { "--network", NULL },
+ { .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
.parse = ipset_parse_net,
},
@@ -36,7 +36,7 @@ static const struct ipset_arg bitmap_ipmac_create_args[] = {
};
static const struct ipset_arg bitmap_ipmac_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
@@ -52,7 +52,7 @@ static const char bitmap_ipmac_usage[] =
struct ipset_type ipset_bitmap_ipmac0 = {
.name = "bitmap:ip,mac",
- .alias = "macipmap",
+ .alias = { "macipmap", NULL },
.revision = 0,
.family = AF_INET,
.dimension = IPSET_DIM_TWO,
diff --git a/src/ipset_bitmap_port.c b/src/ipset_bitmap_port.c
index 11566ce..7871891 100644
--- a/src/ipset_bitmap_port.c
+++ b/src/ipset_bitmap_port.c
@@ -11,20 +11,20 @@
/* Parse commandline arguments */
static const struct ipset_arg bitmap_port_create_args[] = {
- { .name = { "range", "--range", NULL },
+ { .name = { "range", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT,
.parse = ipset_parse_port, .print = ipset_print_port,
},
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
- { .name = { "--from", NULL },
+ { .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT,
.parse = ipset_parse_single_port,
},
- { .name = { "--to", NULL },
+ { .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PORT_TO,
.parse = ipset_parse_single_port,
},
@@ -32,7 +32,7 @@ static const struct ipset_arg bitmap_port_create_args[] = {
};
static const struct ipset_arg bitmap_port_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
@@ -48,7 +48,7 @@ static const char bitmap_port_usage[] =
struct ipset_type ipset_bitmap_port0 = {
.name = "bitmap:port",
- .alias = "portmap",
+ .alias = { "portmap", NULL },
.revision = 0,
.family = AF_UNSPEC,
.dimension = IPSET_DIM_ONE,
diff --git a/src/ipset_hash_ip.c b/src/ipset_hash_ip.c
index 212cb22..6609eea 100644
--- a/src/ipset_hash_ip.c
+++ b/src/ipset_hash_ip.c
@@ -11,35 +11,54 @@
/* Parse commandline arguments */
static const struct ipset_arg hash_ip_create_args[] = {
- { .name = { "hashsize", "--hashsize", NULL },
+ { .name = { "family", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family, .print = ipset_print_family,
+ },
+ /* Alias: family inet */
+ { .name = { "-4", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
+ },
+ /* Alias: family inet6 */
+ { .name = { "-6", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
+ },
+ { .name = { "hashsize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "maxelem", "--maxleme", NULL },
+ { .name = { "maxelem", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "probes", "--probes", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "resize", "--resize", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "netmask", "--netmask", NULL },
+ { .name = { "netmask", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NETMASK,
.parse = ipset_parse_netmask, .print = ipset_print_number,
},
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
+ /* Ignored options: backward compatibilty */
+ { .name = { "probes", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
+ { .name = { "resize", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
+ { .name = { "gc", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_GC,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
{ },
};
static const struct ipset_arg hash_ip_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
@@ -48,8 +67,8 @@ static const struct ipset_arg hash_ip_add_args[] = {
static const char hash_ip_usage[] =
"create SETNAME hash:ip\n"
+" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
-" [probes VALUE] [resize VALUE]\n"
" [netmask CIDR] [timeout VALUE]\n"
"add SETNAME IP|IP/CIDR|FROM-TO [timeout VALUE]\n"
"del SETNAME IP|IP/CIDR|FROM-TO\n"
@@ -57,7 +76,7 @@ static const char hash_ip_usage[] =
struct ipset_type ipset_hash_ip0 = {
.name = "hash:ip",
- .alias = "iphash",
+ .alias = { "iphash", "iptree", "iptreemap", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_ONE,
@@ -68,6 +87,7 @@ struct ipset_type ipset_hash_ip0 = {
.opt = IPSET_OPT_IP
},
},
+ .compat_parse_elem = ipset_parse_iptimeout,
.args = {
[IPSET_CREATE] = hash_ip_create_args,
[IPSET_ADD] = hash_ip_add_args,
@@ -81,8 +101,6 @@ struct ipset_type ipset_hash_ip0 = {
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
- | IPSET_FLAG(IPSET_OPT_PROBES)
- | IPSET_FLAG(IPSET_OPT_RESIZE)
| IPSET_FLAG(IPSET_OPT_NETMASK)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
diff --git a/src/ipset_hash_ipport.c b/src/ipset_hash_ipport.c
index a8cc7ad..4a9b8cf 100644
--- a/src/ipset_hash_ipport.c
+++ b/src/ipset_hash_ipport.c
@@ -11,48 +11,58 @@
/* Parse commandline arguments */
static const struct ipset_arg hash_ipport_create_args[] = {
- { .name = { "range", "--range", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_netrange, .print = ipset_print_ip,
+ { .name = { "family", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family, .print = ipset_print_family,
+ },
+ /* Alias: family inet */
+ { .name = { "-4", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
+ },
+ /* Alias: family inet6 */
+ { .name = { "-6", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
},
- { .name = { "hashsize", "--hashsize", NULL },
+ { .name = { "hashsize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "maxelem", "--maxleme", NULL },
+ { .name = { "maxelem", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "probes", "--probes", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "resize", "--resize", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
- { .name = { "--from", NULL },
+ { .name = { "probes", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
+ { .name = { "resize", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
+ { .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_single_ip,
+ .parse = ipset_parse_ignored,
},
- { .name = { "--to", NULL },
+ { .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
- .parse = ipset_parse_single_ip,
+ .parse = ipset_parse_ignored,
},
- { .name = { "--network", NULL },
+ { .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_net,
+ .parse = ipset_parse_ignored,
},
{ },
};
static const struct ipset_arg hash_ipport_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
@@ -60,10 +70,9 @@ static const struct ipset_arg hash_ipport_add_args[] = {
};
static const char hash_ipport_usage[] =
-"create SETNAME hash:ip,port range IP/CIDR|FROM-TO\n"
+"create SETNAME hash:ip,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
-" [probes VALUE] [resize VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP,PORT [timeout VALUE]\n"
"del SETNAME IP,PORT\n"
@@ -71,7 +80,7 @@ static const char hash_ipport_usage[] =
struct ipset_type ipset_hash_ipport0 = {
.name = "hash:ip,port",
- .alias = "ipporthash",
+ .alias = { "ipporthash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_TWO,
@@ -92,8 +101,7 @@ struct ipset_type ipset_hash_ipport0 = {
[IPSET_ADD] = hash_ipport_add_args,
},
.mandatory = {
- [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_IP_TO),
+ [IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
@@ -102,12 +110,8 @@ struct ipset_type ipset_hash_ipport0 = {
| IPSET_FLAG(IPSET_OPT_PORT),
},
.full = {
- [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_IP_TO)
- | IPSET_FLAG(IPSET_OPT_HASHSIZE)
+ [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
- | IPSET_FLAG(IPSET_OPT_PROBES)
- | IPSET_FLAG(IPSET_OPT_RESIZE)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
diff --git a/src/ipset_hash_ipportip.c b/src/ipset_hash_ipportip.c
index 71ed3c5..299e362 100644
--- a/src/ipset_hash_ipportip.c
+++ b/src/ipset_hash_ipportip.c
@@ -11,48 +11,58 @@
/* Parse commandline arguments */
static const struct ipset_arg hash_ipportip_create_args[] = {
- { .name = { "range", "--range", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_netrange, .print = ipset_print_ip,
+ { .name = { "family", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family, .print = ipset_print_family,
+ },
+ /* Alias: family inet */
+ { .name = { "-4", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
+ },
+ /* Alias: family inet6 */
+ { .name = { "-6", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
},
- { .name = { "hashsize", "--hashsize", NULL },
+ { .name = { "hashsize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "maxelem", "--maxleme", NULL },
+ { .name = { "maxelem", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "probes", "--probes", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "resize", "--resize", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
- { .name = { "--from", NULL },
+ { .name = { "probes", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
+ { .name = { "resize", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
+ { .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_single_ip,
+ .parse = ipset_parse_ignored,
},
- { .name = { "--to", NULL },
+ { .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
- .parse = ipset_parse_single_ip,
+ .parse = ipset_parse_ignored,
},
- { .name = { "--network", NULL },
+ { .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_net,
+ .parse = ipset_parse_ignored,
},
{ },
};
static const struct ipset_arg hash_ipportip_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
@@ -60,10 +70,9 @@ static const struct ipset_arg hash_ipportip_add_args[] = {
};
static const char hash_ipportip_usage[] =
-"create SETNAME hash:ip,port,ip range IP/CIDR|FROM-TO\n"
+"create SETNAME hash:ip,port,ip\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
-" [probes VALUE] [resize VALUE]\n"
" [timeout VALUE]\n"
"add SETNAME IP,PORT,IP [timeout VALUE]\n"
"del SETNAME IP,PORT,IP\n"
@@ -71,7 +80,7 @@ static const char hash_ipportip_usage[] =
struct ipset_type ipset_hash_ipportip0 = {
.name = "hash:ip,port,ip",
- .alias = "ipportiphash",
+ .alias = { "ipportiphash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_THREE,
@@ -97,8 +106,7 @@ struct ipset_type ipset_hash_ipportip0 = {
[IPSET_ADD] = hash_ipportip_add_args,
},
.mandatory = {
- [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_IP_TO),
+ [IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
| IPSET_FLAG(IPSET_OPT_IP2),
@@ -110,12 +118,8 @@ struct ipset_type ipset_hash_ipportip0 = {
| IPSET_FLAG(IPSET_OPT_IP2),
},
.full = {
- [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_IP_TO)
- | IPSET_FLAG(IPSET_OPT_HASHSIZE)
+ [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
- | IPSET_FLAG(IPSET_OPT_PROBES)
- | IPSET_FLAG(IPSET_OPT_RESIZE)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
diff --git a/src/ipset_hash_ipportnet.c b/src/ipset_hash_ipportnet.c
index 769304d..13a0487 100644
--- a/src/ipset_hash_ipportnet.c
+++ b/src/ipset_hash_ipportnet.c
@@ -11,48 +11,58 @@
/* Parse commandline arguments */
static const struct ipset_arg hash_ipportnet_create_args[] = {
- { .name = { "range", "--range", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_netrange, .print = ipset_print_ip,
+ { .name = { "family", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family, .print = ipset_print_family,
+ },
+ /* Alias: family inet */
+ { .name = { "-4", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
+ },
+ /* Alias: family inet6 */
+ { .name = { "-6", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
},
- { .name = { "hashsize", "--hashsize", NULL },
+ { .name = { "hashsize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "maxelem", "--maxleme", NULL },
+ { .name = { "maxelem", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "probes", "--probes", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "resize", "--resize", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
/* Backward compatibility */
- { .name = { "--from", NULL },
+ { .name = { "probes", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
+ { .name = { "resize", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
+ },
+ { .name = { "from", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_single_ip,
+ .parse = ipset_parse_ignored,
},
- { .name = { "--to", NULL },
+ { .name = { "to", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP_TO,
- .parse = ipset_parse_single_ip,
+ .parse = ipset_parse_ignored,
},
- { .name = { "--network", NULL },
+ { .name = { "network", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_IP,
- .parse = ipset_parse_net,
+ .parse = ipset_parse_ignored,
},
{ },
};
static const struct ipset_arg hash_ipportnet_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
@@ -60,18 +70,17 @@ static const struct ipset_arg hash_ipportnet_add_args[] = {
};
static const char hash_ipportnet_usage[] =
-"create SETNAME hash:ip,port,net range IP/CIDR|FROM-TO\n"
+"create SETNAME hash:ip,port,net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
-" [probes VALUE] [resize VALUE]\n"
" [timeout VALUE]\n"
-"add SETNAME IP,PORT,IP/CIDR [timeout VALUE]\n"
-"del SETNAME IP,PORT,IP/CIDR\n"
-"test SETNAME IP,PORT,IP/CIDR\n";
+"add SETNAME IP,PORT,IP[/CIDR] [timeout VALUE]\n"
+"del SETNAME IP,PORT,IP[/CIDR]\n"
+"test SETNAME IP,PORT,IP[/CIDR]\n";
struct ipset_type ipset_hash_ipportnet0 = {
.name = "hash:ip,port,net",
- .alias = "ipportnethash",
+ .alias = { "ipportnethash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_THREE,
@@ -87,7 +96,7 @@ struct ipset_type ipset_hash_ipportnet0 = {
.opt = IPSET_OPT_PORT
},
[IPSET_DIM_THREE] = {
- .parse = ipset_parse_net,
+ .parse = ipset_parse_ipnet,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP2
},
@@ -97,28 +106,20 @@ struct ipset_type ipset_hash_ipportnet0 = {
[IPSET_ADD] = hash_ipportnet_add_args,
},
.mandatory = {
- [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_IP_TO),
+ [IPSET_CREATE] = 0,
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
- | IPSET_FLAG(IPSET_OPT_IP2)
- | IPSET_FLAG(IPSET_OPT_CIDR2),
+ | IPSET_FLAG(IPSET_OPT_IP2),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
- | IPSET_FLAG(IPSET_OPT_IP2)
- | IPSET_FLAG(IPSET_OPT_CIDR2),
+ | IPSET_FLAG(IPSET_OPT_IP2),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
- | IPSET_FLAG(IPSET_OPT_IP2)
- | IPSET_FLAG(IPSET_OPT_CIDR2),
+ | IPSET_FLAG(IPSET_OPT_IP2),
},
.full = {
- [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_IP_TO)
- | IPSET_FLAG(IPSET_OPT_HASHSIZE)
+ [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
- | IPSET_FLAG(IPSET_OPT_PROBES)
- | IPSET_FLAG(IPSET_OPT_RESIZE)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
diff --git a/src/ipset_hash_net.c b/src/ipset_hash_net.c
index 1d70900..c14652d 100644
--- a/src/ipset_hash_net.c
+++ b/src/ipset_hash_net.c
@@ -11,31 +11,46 @@
/* Parse commandline arguments */
static const struct ipset_arg hash_net_create_args[] = {
- { .name = { "hashsize", "--hashsize", NULL },
+ { .name = { "family", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family, .print = ipset_print_family,
+ },
+ /* Alias: family inet */
+ { .name = { "-4", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
+ },
+ /* Alias: family inet6 */
+ { .name = { "-6", NULL },
+ .has_arg = IPSET_NO_ARG, .opt = IPSET_OPT_FAMILY,
+ .parse = ipset_parse_family,
+ },
+ { .name = { "hashsize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_HASHSIZE,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "maxelem", "--maxleme", NULL },
+ { .name = { "maxelem", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "probes", "--probes", NULL },
+ { .name = { "timeout", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
+ .parse = ipset_parse_uint32, .print = ipset_print_number,
+ },
+ /* Ignored options: backward compatibilty */
+ { .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
},
- { .name = { "resize", "--resize", NULL },
+ { .name = { "resize", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_RESIZE,
- .parse = ipset_parse_uint8, .print = ipset_print_number,
- },
- { .name = { "timeout", "--timeout", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
- .parse = ipset_parse_uint32, .print = ipset_print_number,
+ .parse = ipset_parse_ignored, .print = ipset_print_number,
},
{ },
};
static const struct ipset_arg hash_net_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
@@ -46,21 +61,20 @@ static const char hash_net_usage[] =
"create SETNAME hash:net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
-" [probes VALUE] [resize VALUE]\n"
" [timeout VALUE]\n"
-"add SETNAME IP/CIDR [timeout VALUE]\n"
-"del SETNAME IP/CIDR\n"
-"test SETNAME IP/CIDR\n";
+"add SETNAME IP[/CIDR] [timeout VALUE]\n"
+"del SETNAME IP[/CIDR]\n"
+"test SETNAME IP[/CIDR]\n";
struct ipset_type ipset_hash_net0 = {
.name = "hash:net",
- .alias = "nethash",
+ .alias = { "nethash", NULL },
.revision = 0,
.family = AF_INET46,
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
- .parse = ipset_parse_net,
+ .parse = ipset_parse_ipnet,
.print = ipset_print_ip,
.opt = IPSET_OPT_IP
},
@@ -71,18 +85,13 @@ struct ipset_type ipset_hash_net0 = {
},
.mandatory = {
[IPSET_CREATE] = 0,
- [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_CIDR),
- [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_CIDR),
- [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_CIDR),
+ [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP),
+ [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP),
+ [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
},
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
- | IPSET_FLAG(IPSET_OPT_PROBES)
- | IPSET_FLAG(IPSET_OPT_RESIZE)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_CIDR)
diff --git a/src/ipset_iptreemap.c b/src/ipset_iptreemap.c
deleted file mode 100644
index 22bdcb3..0000000
--- a/src/ipset_iptreemap.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/* Copyright 2007 Sven Wegener <sven.wegener@stealer.net>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <limits.h> /* UINT_MAX */
-#include <stdio.h> /* *printf */
-#include <string.h> /* mem* */
-
-#include "ipset.h"
-
-#include <linux/netfilter_ipv4/ip_set_iptreemap.h>
-
-#define OPT_CREATE_GC 0x1
-
-static void
-iptreemap_create_init(void *data)
-{
- struct ip_set_req_iptreemap_create *mydata = data;
-
- mydata->gc_interval = 0;
-}
-
-static int
-iptreemap_create_parse(int c, char *argv[] UNUSED, void *data,
- unsigned int *flags)
-{
- struct ip_set_req_iptreemap_create *mydata = data;
-
- switch (c) {
- case 'g':
- string_to_number(optarg, 0, UINT_MAX, &mydata->gc_interval);
-
- *flags |= OPT_CREATE_GC;
- break;
- default:
- return 0;
- break;
- }
-
- return 1;
-}
-
-static void
-iptreemap_create_final(void *data UNUSED, unsigned int flags UNUSED)
-{
-}
-
-static const struct option create_opts[] = {
- {.name = "gc", .has_arg = required_argument, .val = 'g'},
- {NULL},
-};
-
-static ip_set_ip_t
-iptreemap_adt_parser(int cmd UNUSED, const char *arg, void *data)
-{
- struct ip_set_req_iptreemap *mydata = data;
- ip_set_ip_t mask;
-
- char *saved = ipset_strdup(arg);
- char *ptr, *tmp = saved;
-
- if (strchr(tmp, '/')) {
- parse_ipandmask(tmp, &mydata->ip, &mask);
- mydata->end = mydata->ip | ~mask;
- } else {
- if ((ptr = strchr(tmp, ':')) != NULL && ++warn_once == 1)
- fprintf(stderr, "Warning: please use '-' separator token between IP range.\n"
- "Next release won't support old separator token.\n");
- ptr = strsep(&tmp, "-:");
- parse_ip(ptr, &mydata->ip);
-
- if (tmp) {
- parse_ip(tmp, &mydata->end);
- } else {
- mydata->end = mydata->ip;
- }
- }
-
- ipset_free(saved);
-
- return 1;
-}
-
-static void
-iptreemap_initheader(struct set *set, const void *data)
-{
- const struct ip_set_req_iptreemap_create *header = data;
- struct ip_set_iptreemap *map = set->settype->header;
-
- map->gc_interval = header->gc_interval;
-}
-
-static void
-iptreemap_printheader(struct set *set, unsigned int options UNUSED)
-{
- struct ip_set_iptreemap *mysetdata = set->settype->header;
-
- if (mysetdata->gc_interval)
- printf(" gc: %u", mysetdata->gc_interval);
-
- printf("\n");
-}
-
-static void
-iptreemap_printips_sorted(struct set *set UNUSED, void *data,
- u_int32_t len, unsigned int options, char dont_align)
-{
- struct ip_set_req_iptreemap *req;
- size_t offset = 0;
-
- while (len >= offset + sizeof(struct ip_set_req_iptreemap)) {
- req = data + offset;
-
- printf("%s", ip_tostring(req->ip, options));
- if (req->ip != req->end)
- printf("-%s", ip_tostring(req->end, options));
- printf("\n");
-
- offset += IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align);
- }
-}
-
-static void
-iptreemap_saveheader(struct set *set, unsigned int options UNUSED)
-{
- struct ip_set_iptreemap *mysetdata = set->settype->header;
-
- printf("-N %s %s", set->name, set->settype->typename);
-
- if (mysetdata->gc_interval)
- printf(" --gc %u", mysetdata->gc_interval);
-
- printf("\n");
-}
-
-static void
-iptreemap_saveips(struct set *set UNUSED, void *data,
- u_int32_t len, unsigned int options, char dont_align)
-{
- struct ip_set_req_iptreemap *req;
- size_t offset = 0;
-
- while (len >= offset + sizeof(struct ip_set_req_iptreemap)) {
- req = data + offset;
-
- printf("-A %s %s", set->name, ip_tostring(req->ip, options));
-
- if (req->ip != req->end)
- printf("-%s", ip_tostring(req->end, options));
-
- printf("\n");
-
- offset += IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align);
- }
-}
-
-static void
-iptreemap_usage(void)
-{
- printf(
- "-N set iptreemap --gc interval\n"
- "-A set IP\n"
- "-D set IP\n"
- "-T set IP\n"
- );
-}
-
-static struct settype settype_iptreemap = {
- .typename = SETTYPE_NAME,
- .protocol_version = IP_SET_PROTOCOL_VERSION,
-
- .create_size = sizeof(struct ip_set_req_iptreemap_create),
- .create_init = iptreemap_create_init,
- .create_parse = iptreemap_create_parse,
- .create_final = iptreemap_create_final,
- .create_opts = create_opts,
-
- .adt_size = sizeof(struct ip_set_req_iptreemap),
- .adt_parser = iptreemap_adt_parser,
-
- .header_size = sizeof(struct ip_set_iptreemap),
- .initheader = iptreemap_initheader,
- .printheader = iptreemap_printheader,
- .printips = iptreemap_printips_sorted,
- .printips_sorted = iptreemap_printips_sorted,
- .saveheader = iptreemap_saveheader,
- .saveips = iptreemap_saveips,
-
- .usage = iptreemap_usage,
-};
-
-CONSTRUCTOR(iptreemap)
-{
- settype_register(&settype_iptreemap);
-}
diff --git a/src/ipset_list_set.c b/src/ipset_list_set.c
index 11b2523..76cf9b2 100644
--- a/src/ipset_list_set.c
+++ b/src/ipset_list_set.c
@@ -11,48 +11,59 @@
/* Parse commandline arguments */
static const struct ipset_arg list_set_create_args[] = {
- { .name = { "size", "--size", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_MAXELEM,
- .parse = ipset_parse_uint32, .print = ipset_print_ip,
+ { .name = { "size", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_SIZE,
+ .parse = ipset_parse_uint32, .print = ipset_print_number,
},
- { .name = { "timeout", "--timeout", NULL },
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
{ },
};
-static const struct ipset_arg list_set_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
+static const struct ipset_arg list_set_adt_args[] = {
+ { .name = { "timeout", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
+ { .name = { "before", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NAMEREF,
+ .parse = ipset_parse_before,
+ },
+ { .name = { "after", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_NAMEREF,
+ .parse = ipset_parse_after,
+ },
{ },
};
static const char list_set_usage[] =
"create SETNAME list:set\n"
" [size VALUE] [timeout VALUE]\n"
-"add SETNAME NAME[,before|after,NAME] [timeout VALUE]\n"
+"add SETNAME NAME [before|after NAME] [timeout VALUE]\n"
"del SETNAME NAME\n"
"test SETNAME NAME\n";
struct ipset_type ipset_list_set0 = {
.name = "list:set",
- .alias = "setlist",
+ .alias = { "setlist", NULL },
.revision = 0,
.family = AF_UNSPEC,
.dimension = IPSET_DIM_ONE,
.elem = {
[IPSET_DIM_ONE] = {
- .parse = ipset_parse_name,
+ .parse = ipset_parse_setname,
.print = ipset_print_name,
.opt = IPSET_OPT_NAME
},
},
+ .compat_parse_elem = ipset_parse_name_compat,
.args = {
[IPSET_CREATE] = list_set_create_args,
- [IPSET_ADD] = list_set_add_args,
+ [IPSET_ADD] = list_set_adt_args,
+ [IPSET_DEL] = list_set_adt_args,
+ [IPSET_TEST] = list_set_adt_args,
},
.mandatory = {
[IPSET_CREATE] = 0,
diff --git a/src/ipset_tree_ip.c b/src/ipset_tree_ip.c
deleted file mode 100644
index 7f7d842..0000000
--- a/src/ipset_tree_ip.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <libipset/data.h> /* IPSET_OPT_* */
-#include <libipset/parse.h> /* parser functions */
-#include <libipset/print.h> /* printing functions */
-#include <libipset/types.h> /* prototypes */
-
-/* Parse commandline arguments */
-static const struct ipset_arg tree_ip_create_args[] = {
- { .name = { "gc", "--gc", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_GC,
- .parse = ipset_parse_uint32, .print = ipset_print_number,
- },
- { .name = { "timeout", "--timeout", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
- .parse = ipset_parse_uint32, .print = ipset_print_number,
- },
- { },
-};
-
-static const struct ipset_arg tree_ip_add_args[] = {
- { .name = { "timeout", "--timeout", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
- .parse = ipset_parse_uint32, .print = ipset_print_number,
- },
- { },
-};
-
-static const char tree_ip_usage[] =
-"create SETNAME tree:ip\n"
-" [gc VALUE] [timeout VALUE]\n"
-"add SETNAME IP|IP/CIDR|FROM-TO [timeout VALUE]\n"
-"del SETNAME IP|IP/CIDR|FROM-TO\n"
-"test SETNAME IP\n";
-
-struct ipset_type ipset_tree_ip0 = {
- .name = "tree:ip",
- .alias = "iptree",
- .revision = 0,
- .family = AF_INET,
- .dimension = IPSET_DIM_ONE,
- .elem = {
- [IPSET_DIM_ONE] = {
- .parse = ipset_parse_ip,
- .print = ipset_print_ip,
- .opt = IPSET_OPT_IP
- },
- },
- .args = {
- [IPSET_CREATE] = tree_ip_create_args,
- [IPSET_ADD] = tree_ip_add_args,
- },
- .mandatory = {
- [IPSET_CREATE] = 0,
- [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP),
- [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP),
- [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
- },
- .full = {
- [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_GC)
- | IPSET_FLAG(IPSET_OPT_TIMEOUT),
- [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_IP_TO)
- | IPSET_FLAG(IPSET_OPT_TIMEOUT),
- [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_IP_TO),
- [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
- },
-
- .usage = tree_ip_usage,
-};
diff --git a/src/ui.c b/src/ui.c
index bc01e61..d8face5 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -4,107 +4,155 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <assert.h> /* assert */
+#include <ctype.h> /* tolower */
+#include <string.h> /* memcmp, str* */
+
#include <libipset/linux_ip_set.h> /* IPSET_CMD_* */
#include <libipset/types.h> /* IPSET_*_ARG */
#include <libipset/session.h> /* ipset_envopt_parse */
#include <libipset/parse.h> /* ipset_parse_family */
#include <libipset/print.h> /* ipset_print_family */
+#include <libipset/utils.h> /* STREQ */
#include <libipset/ui.h> /* prototypes */
/* Commands and environment options */
const struct ipset_commands ipset_commands[] = {
- [IPSET_CMD_CREATE - 1] = {
- .name = { "create", "c", "-N", "--create", NULL },
+ /* Order is important */
+
+ { /* c[reate], --create, n, -N */
+ .cmd = IPSET_CMD_CREATE,
+ .name = { "create", "n" },
.has_arg = IPSET_MANDATORY_ARG2,
.help = "SETNAME TYPENAME [type-specific-options]\n"
" Create a new set",
},
- [IPSET_CMD_DESTROY - 1] = {
- .name = { "destroy", "x", "-X", "--destroy", NULL },
- .has_arg = IPSET_OPTIONAL_ARG,
- .help = "[SETNAME]\n"
- " Destroy a named set or all sets",
- },
- [IPSET_CMD_FLUSH - 1] = {
- .name = { "flush", "f", "-F", "--flush", NULL },
- .has_arg = IPSET_OPTIONAL_ARG,
- .help = "[SETNAME]\n"
- " Flush a named set or all sets",
+ { /* a[dd], --add, -A */
+ .cmd = IPSET_CMD_ADD,
+ .name = { "add", NULL },
+ .has_arg = IPSET_MANDATORY_ARG2,
+ .help = "SETNAME ENTRY\n"
+ " Add entry to the named set",
},
- [IPSET_CMD_RENAME - 1] = {
- .name = { "rename", "e", "-E", "--rename", NULL },
+ { /* d[el], --del, -D */
+ .cmd = IPSET_CMD_DEL,
+ .name = { "del", NULL },
.has_arg = IPSET_MANDATORY_ARG2,
- .help = "FROM-SETNAME TO-SETNAME\n"
- " Rename two sets",
+ .help = "SETNAME ENTRY\n"
+ " Delete entry from the named set",
},
- [IPSET_CMD_SWAP - 1] = {
- .name = { "swap", "w", "-W", "--swap", NULL },
+ { /* t[est], --test, -T */
+ .cmd = IPSET_CMD_TEST,
+ .name = { "test", NULL },
.has_arg = IPSET_MANDATORY_ARG2,
- .help = "FROM-SETNAME TO-SETNAME\n"
- " Swap the contect of two existing sets",
+ .help = "SETNAME ENTRY\n"
+ " Test entry in the named set",
+ },
+ { /* des[troy], --destroy, x, -X */
+ .cmd = IPSET_CMD_DESTROY,
+ .name = { "destroy", "x" },
+ .has_arg = IPSET_OPTIONAL_ARG,
+ .help = "[SETNAME]\n"
+ " Destroy a named set or all sets",
},
- [IPSET_CMD_LIST - 1] = {
- .name = { "list", "l", "-L", "--list", NULL },
+ { /* l[ist], --list, -L */
+ .cmd = IPSET_CMD_LIST,
+ .name = { "list", NULL },
.has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n"
" List the entries of a named set or all sets",
},
- [IPSET_CMD_SAVE - 1] = {
- .name = { "save", "s", "-S", "--save", NULL },
+ { /* s[save], --save, -S */
+ .cmd = IPSET_CMD_SAVE,
+ .name = { "save", NULL },
.has_arg = IPSET_OPTIONAL_ARG,
.help = "[SETNAME]\n"
" Save the named set or all sets to stdout",
},
- [IPSET_CMD_ADD - 1] = {
- .name = { "add", "a", "-A", "--add", NULL },
- .has_arg = IPSET_MANDATORY_ARG2,
- .help = "SETNAME ENTRY\n"
- " Add entry to a named set",
+ { /* r[estore], --restore, -R */
+ .cmd = IPSET_CMD_RESTORE,
+ .name = { "restore", NULL },
+ .has_arg = IPSET_NO_ARG,
+ .help = "\n"
+ " Restore a saved state",
+ },
+ { /* f[lush], --flush, -F */
+ .cmd = IPSET_CMD_FLUSH,
+ .name = { "flush", NULL },
+ .has_arg = IPSET_OPTIONAL_ARG,
+ .help = "[SETNAME]\n"
+ " Flush a named set or all sets",
},
- [IPSET_CMD_DEL - 1] = {
- .name = { "del", "d", "-D", "--del", NULL },
+ { /* ren[ame], --rename, e, -E */
+ .cmd = IPSET_CMD_RENAME,
+ .name = { "rename", "e" },
.has_arg = IPSET_MANDATORY_ARG2,
- .help = "SETNAME ENTRY\n"
- " Delete entry from a named set",
+ .help = "FROM-SETNAME TO-SETNAME\n"
+ " Rename two sets",
},
- [IPSET_CMD_TEST - 1] = {
- .name = { "test", "t", "-T", "--test", NULL },
+ { /* sw[ap], --swap, w, -W */
+ .cmd = IPSET_CMD_SWAP,
+ .name = { "swap", "w" },
.has_arg = IPSET_MANDATORY_ARG2,
- .help = "SETNAME ENTRY\n"
- " Test if entry exists in the named set",
+ .help = "FROM-SETNAME TO-SETNAME\n"
+ " Swap the contect of two existing sets",
},
- [IPSET_CMD_HELP - 1] = {
- .name = { "help", "h", "-H", "-h", "--help", NULL },
+ { /* h[elp, --help, -H */
+ .cmd = IPSET_CMD_HELP,
+ .name = { "help", NULL },
.has_arg = IPSET_OPTIONAL_ARG,
.help = "[TYPENAME]\n"
" Print help, and settype specific help",
},
- [IPSET_CMD_RESTORE - 1] = {
- .name = { "restore", "r", "-R", "--restore", NULL },
+ { /* v[ersion], --version, -V */
+ .cmd = IPSET_CMD_VERSION,
+ .name = { "version", NULL },
.has_arg = IPSET_NO_ARG,
.help = "\n"
- " Restore a saved state",
+ " Print version information",
},
- [IPSET_CMD_VERSION - 1] = {
- .name = { "version", "v", "-V", "-v", "--version", NULL },
+ { /* q[uit] */
+ .cmd = IPSET_CMD_QUIT,
+ .name = { "quit", NULL },
.has_arg = IPSET_NO_ARG,
.help = "\n"
- " Print version information",
+ " Quit interactive mode",
},
- [IPSET_CMD_MAX - 1] = { },
+ { },
};
+/* Match a command: try to match as a prefix or letter-command */
+bool
+ipset_match_cmd(const char *arg, const char * const name[])
+{
+ size_t len;
+
+ assert(arg);
+ assert(name && name[0]);
+
+ /* Ignore (two) leading dashes */
+ if (arg[0] == '-')
+ arg++;
+ if (arg[0] == '-')
+ arg++;
+
+ len = strlen(arg);
+
+ if (len > strlen(name[0]) || !len)
+ return false;
+ else if (memcmp(arg, name[0], len) == 0)
+ return true;
+ else if (len != 1)
+ return false;
+ else if (name[1] == NULL)
+ return tolower(arg[0]) == name[0][0];
+ else
+ return tolower(arg[0]) == name[1][0];
+}
+
const struct ipset_envopts ipset_envopts[] = {
- { .name = { "family", "--family", NULL },
- .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_FAMILY,
- .parse = ipset_parse_family, .print = ipset_print_family,
- .help = "inet|inet6\n"
- " Specify family when creating a set\n"
- " which supports multiple families.\n"
- " The default family is INET.",
- },
- { .name = { "-o", "--output", NULL },
+ { .name = { "-o", "-output" },
.has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX,
.parse = ipset_parse_output,
.help = "plain|save|xml\n"
@@ -112,25 +160,25 @@ const struct ipset_envopts ipset_envopts[] = {
" Default value for \"list\" command is mode \"plain\"\n"
" and for \"save\" command is mode \"save\".",
},
- { .name = { "-s", "--sorted", NULL },
+ { .name = { "-s", "-sorted" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_SORTED,
.help = "\n"
" Print elements sorted (if supported by the set type).",
},
- { .name = { "-q", "--quiet", NULL },
+ { .name = { "-q", "-quiet" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_QUIET,
.help = "\n"
" Suppress any notice or warning message.",
},
- { .name = { "-r", "--resolve", NULL },
+ { .name = { "-r", "-resolve" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_RESOLVE,
.help = "\n"
" Try to resolve IP addresses in the output (slow!)",
},
- { .name = { "-x", "--exist", NULL },
+ { .name = { "-!", "-exist" },
.parse = ipset_envopt_parse,
.has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_EXIST,
.help = "\n"
@@ -138,14 +186,59 @@ const struct ipset_envopts ipset_envopts[] = {
" when adding already existing elements\n"
" or when deleting non-existing elements.",
},
- /* Aliases */
- { .name = { "-4", NULL },
- .has_arg = IPSET_NO_ARG, .flag = IPSET_OPT_FAMILY,
- .parse = ipset_parse_family,
- },
- { .name = { "-6", NULL },
- .has_arg = IPSET_NO_ARG, .flag = IPSET_OPT_FAMILY,
- .parse = ipset_parse_family,
- },
{ },
};
+
+/* Strict option matching */
+bool
+ipset_match_option(const char *arg, const char * const name[])
+{
+ assert(arg);
+ assert(name && name[0]);
+
+ /* Skip two leading dashes */
+ if (arg[0] == '-' && arg[1] == '-')
+ arg++, arg++;
+
+ return STREQ(arg, name[0])
+ || (name[1] != NULL && STREQ(arg, name[1]));
+}
+
+/* Strict envopt matching */
+bool
+ipset_match_envopt(const char *arg, const char * const name[])
+{
+ assert(arg);
+ assert(name && name[0]);
+
+ /* Skip one leading dash */
+ if (arg[0] == '-' && arg[1] == '-')
+ arg++;
+
+ return STREQ(arg, name[0])
+ || (name[1] != NULL && STREQ(arg, name[1]));
+}
+
+/**
+ * ipset_shift_argv - shift off an argument
+ * @arc: argument count
+ * @argv: array of argument strings
+ * @from: from where shift off an argument
+ *
+ * Shift off the argument at "from" from the array of
+ * arguments argv of size argc.
+ */
+void
+ipset_shift_argv(int *argc, char *argv[], int from)
+{
+ int i;
+
+ assert(*argc >= from + 1);
+
+ for (i = from + 1; i <= *argc; i++) {
+ argv[i-1] = argv[i];
+ }
+ (*argc)--;
+ return;
+}
+
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..c9fcefc
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,450 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# tests/Makefile. Generated from Makefile.in by configure.
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+pkgdatadir = $(datadir)/ipset
+pkglibdir = $(libdir)/ipset
+pkgincludedir = $(includedir)/ipset
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = x86_64-unknown-linux-gnu
+host_triplet = x86_64-unknown-linux-gnu
+target_triplet = x86_64-unknown-linux-gnu
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(top_srcdir)/Make_global.am
+subdir = tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /root/ipset/missing --run aclocal-1.10
+AMTAR = ${SHELL} /root/ipset/missing --run tar
+AR = ar
+AUTOCONF = ${SHELL} /root/ipset/missing --run autoconf
+AUTOHEADER = ${SHELL} /root/ipset/missing --run autoheader
+AUTOMAKE = ${SHELL} /root/ipset/missing --run automake-1.10
+AWK = mawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -g -O2
+CPP = gcc -E
+CPPFLAGS =
+CXX = g++
+CXXCPP =
+CXXDEPMODE = depmode=none
+CXXFLAGS =
+CYGPATH_W = echo
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+DSYMUTIL =
+ECHO = echo
+ECHO_C =
+ECHO_N = -n
+ECHO_T =
+EGREP = /bin/grep -E
+EXEEXT =
+F77 =
+FFLAGS =
+GREP = /bin/grep
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+LDFLAGS =
+LIBOBJS =
+LIBS = -lmnl
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LN_S = ln -s
+LTLIBOBJS =
+MAKEINFO = ${SHELL} /root/ipset/missing --run makeinfo
+MKDIR_P = /bin/mkdir -p
+NMEDIT =
+OBJEXT = o
+PACKAGE = ipset
+PACKAGE_BUGREPORT = kadlec@blackhole.kfki.hu
+PACKAGE_NAME = ipset
+PACKAGE_STRING = ipset 5.0
+PACKAGE_TARNAME = ipset
+PACKAGE_VERSION = 5.0
+PATH_SEPARATOR = :
+RANLIB = ranlib
+SED = /bin/sed
+SET_MAKE =
+SHELL = /bin/bash
+STRIP = strip
+VERSION = 5.0
+abs_builddir = /root/ipset/tests
+abs_srcdir = /root/ipset/tests
+abs_top_builddir = /root/ipset
+abs_top_srcdir = /root/ipset
+ac_ct_CC = gcc
+ac_ct_CXX =
+ac_ct_F77 =
+am__include = include
+am__leading_dot = .
+am__quote =
+am__tar = ${AMTAR} chof - "$$tardir"
+am__untar = ${AMTAR} xf -
+bindir = ${exec_prefix}/bin
+build = x86_64-unknown-linux-gnu
+build_alias =
+build_cpu = x86_64
+build_os = linux-gnu
+build_vendor = unknown
+builddir = .
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host = x86_64-unknown-linux-gnu
+host_alias =
+host_cpu = x86_64
+host_os = linux-gnu
+host_vendor = unknown
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = $(SHELL) /root/ipset/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+mandir = ${datarootdir}/man
+mkdir_p = /bin/mkdir -p
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+sysconfdir = ${prefix}/etc
+target = x86_64-unknown-linux-gnu
+target_alias =
+target_cpu = x86_64
+target_os = linux-gnu
+target_vendor = unknown
+top_builddir = ..
+top_srcdir = ..
+
+# This is _NOT_ the library release version, it's an API version.
+# Please read Chapter 6 "Library interface versions" of the libtool
+# documentation before making any modification
+# http://sources.redhat.com/autobook/autobook/autobook_91.html
+LIBVERSION = 1:0:0
+AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include \
+ -I/usr/local/include
+
+# -Wconversion \ -> false warnings
+# -Wcast-qual \ -> false warnings
+# -Wpointer-arith \ -> we need it
+# -std=gnu99
+
+# -std=gnu99 ULLONG_MAX requires
+AM_CFLAGS = -std=gnu99 \
+ -Wall \
+ -Wextra \
+ -Waggregate-return \
+ -Wbad-function-cast \
+ -Wcast-align \
+ -Wfloat-equal \
+ -Winit-self \
+ -Winline \
+ -Wmissing-declarations \
+ -Wmissing-format-attribute \
+ -Wmissing-prototypes \
+ -Wnested-externs \
+ -Wold-style-definition \
+ -Wpacked \
+ -Wredundant-decls \
+ -Wshadow \
+ -Wsign-compare \
+ -Wstrict-prototypes \
+ -Wswitch-default \
+ -Wundef \
+ -Wwrite-strings \
+ -Wno-missing-field-initializers \
+ -Werror \
+ -g -ggdb
+
+
+# fails with ntoh*:
+# -Wunreachable-code
+AM_VERBOSE_CC = @echo " CC " $@;
+AM_VERBOSE_CCLD = @echo " CCLD " $@;
+TESTS = ./runtest.sh
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Make_global.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; ws='[ ]'; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *$$ws$$tst$$ws*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ echo "XPASS: $$tst"; \
+ ;; \
+ *) \
+ echo "PASS: $$tst"; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *$$ws$$tst$$ws*) \
+ xfail=`expr $$xfail + 1`; \
+ echo "XFAIL: $$tst"; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ echo "FAIL: $$tst"; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ echo "SKIP: $$tst"; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="All $$all tests passed"; \
+ else \
+ banner="All $$all tests behaved as expected ($$xfail expected failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all tests failed"; \
+ else \
+ banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ skipped="($$skip tests were not run)"; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-TESTS check-am clean clean-generic \
+ clean-libtool distclean distclean-generic distclean-libtool \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ uninstall uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..9b62557
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,5 @@
+## Process this file with automake to produce Makefile.in
+
+include $(top_srcdir)/Make_global.am
+
+TESTS = ./runtest.sh
diff --git a/tests/Makefile.in b/tests/Makefile.in
new file mode 100644
index 0000000..5c47bc5
--- /dev/null
+++ b/tests/Makefile.in
@@ -0,0 +1,450 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(top_srcdir)/Make_global.am
+subdir = tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NMEDIT = @NMEDIT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This is _NOT_ the library release version, it's an API version.
+# Please read Chapter 6 "Library interface versions" of the libtool
+# documentation before making any modification
+# http://sources.redhat.com/autobook/autobook/autobook_91.html
+LIBVERSION = 1:0:0
+AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include \
+ -I/usr/local/include
+
+# -Wconversion \ -> false warnings
+# -Wcast-qual \ -> false warnings
+# -Wpointer-arith \ -> we need it
+# -std=gnu99
+
+# -std=gnu99 ULLONG_MAX requires
+AM_CFLAGS = -std=gnu99 \
+ -Wall \
+ -Wextra \
+ -Waggregate-return \
+ -Wbad-function-cast \
+ -Wcast-align \
+ -Wfloat-equal \
+ -Winit-self \
+ -Winline \
+ -Wmissing-declarations \
+ -Wmissing-format-attribute \
+ -Wmissing-prototypes \
+ -Wnested-externs \
+ -Wold-style-definition \
+ -Wpacked \
+ -Wredundant-decls \
+ -Wshadow \
+ -Wsign-compare \
+ -Wstrict-prototypes \
+ -Wswitch-default \
+ -Wundef \
+ -Wwrite-strings \
+ -Wno-missing-field-initializers \
+ -Werror \
+ -g -ggdb
+
+
+# fails with ntoh*:
+# -Wunreachable-code
+@VERBOSE_FALSE@AM_VERBOSE_CC = @echo " CC " $@;
+@VERBOSE_FALSE@AM_VERBOSE_CCLD = @echo " CCLD " $@;
+TESTS = ./runtest.sh
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Make_global.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; ws='[ ]'; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *$$ws$$tst$$ws*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ echo "XPASS: $$tst"; \
+ ;; \
+ *) \
+ echo "PASS: $$tst"; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *$$ws$$tst$$ws*) \
+ xfail=`expr $$xfail + 1`; \
+ echo "XFAIL: $$tst"; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ echo "FAIL: $$tst"; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ echo "SKIP: $$tst"; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="All $$all tests passed"; \
+ else \
+ banner="All $$all tests behaved as expected ($$xfail expected failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all tests failed"; \
+ else \
+ banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ skipped="($$skip tests were not run)"; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-TESTS check-am clean clean-generic \
+ clean-libtool distclean distclean-generic distclean-libtool \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ uninstall uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/tests/bitmap:ip.t b/tests/bitmap:ip.t
index b44f5c4..03cbe0d 100644
--- a/tests/bitmap:ip.t
+++ b/tests/bitmap:ip.t
@@ -23,21 +23,21 @@
# Range: Delete element not added to the set
1 ipset -D test 2.0.0.2
# Range: Delete element not added to the set, with exist flag
-0 ipset -x -D test 2.0.0.2
+0 ipset -! -D test 2.0.0.2
# Range: Add element in the middle
0 ipset -A test 2.0.0.128
# Range: Add element in the middle again
1 ipset -A test 2.0.0.128
# Range: Add element in the middle again, with exist flag
-0 ipset -x -A test 2.0.0.128
+0 ipset -! -A test 2.0.0.128
# Range: Delete the same element
0 ipset -D test 2.0.0.128
# Range: Add a range of elements
0 ipset -A test 2.0.0.128-2.0.0.131 timeout 6
# Range: List set
-0 ipset list test > .foo
+0 ipset list test | sed 's/timeout ./timeout x/' > .foo
# Range: Check listing
-0 grep '2.0.0.1 timeout' .foo >/dev/null
+0 diff .foo bitmap:ip.t.list4 && rm .foo
# Sleep 10s so that entries can time out
0 sleep 10s
# Range: List set after timeout
@@ -77,9 +77,9 @@
# Network: Delete the same element
0 ipset -D test 2.0.0.128
# Network: List set
-0 ipset list test > .foo
+0 ipset list test | sed 's/timeout ./timeout x/' > .foo
# Network: Check listing
-0 grep '2.0.255.255 timeout' .foo >/dev/null
+0 diff .foo bitmap:ip.t.list5 && rm .foo
# Sleep 10s so that entries can time out
0 sleep 10s
# Network: List set
@@ -118,8 +118,10 @@
0 ipset -D test 10.2.0.0
# Subnets: Add a subnet of subnets
0 ipset -A test 10.8.0.0/16 timeout 8
+# Subnets: List set
+0 ipset list test | sed 's/timeout ./timeout x/' > .foo
# Subnets: Check listing
-0 ipset list test | grep '10.0.0.0 timeout' >/dev/null
+0 diff .foo bitmap:ip.t.list6 && rm .foo
# Sleep 10s so that entries can time out
0 sleep 10s
# Subnets: List set
diff --git a/tests/bitmap:ip.t.list0 b/tests/bitmap:ip.t.list0
index 0be60c0..5f6b35d 100644
--- a/tests/bitmap:ip.t.list0
+++ b/tests/bitmap:ip.t.list0
@@ -1,8 +1,7 @@
Name: test
Type: bitmap:ip
Header: range 2.0.0.1-2.1.0.0 timeout 5
-Elements: 1
-Size in memory: 524288
+Size in memory: 524408
References: 0
Members:
2.1.0.0 timeout 0
diff --git a/tests/bitmap:ip.t.list1 b/tests/bitmap:ip.t.list1
index 02ccdaa..6064717 100644
--- a/tests/bitmap:ip.t.list1
+++ b/tests/bitmap:ip.t.list1
@@ -1,8 +1,7 @@
Name: test
Type: bitmap:ip
Header: range 2.0.0.0-2.0.255.255 timeout 5
-Elements: 1
-Size in memory: 524288
+Size in memory: 524408
References: 0
Members:
2.0.0.0 timeout 0
diff --git a/tests/bitmap:ip.t.list2 b/tests/bitmap:ip.t.list2
index 7b17999..e00abc9 100644
--- a/tests/bitmap:ip.t.list2
+++ b/tests/bitmap:ip.t.list2
@@ -1,8 +1,7 @@
Name: test
Type: bitmap:ip
Header: range 10.0.0.0-10.255.255.255 netmask 24 timeout 5
-Elements: 1
-Size in memory: 524288
+Size in memory: 524408
References: 0
Members:
10.255.255.0 timeout 0
diff --git a/tests/bitmap:ip.t.list3 b/tests/bitmap:ip.t.list3
index 677bb2a..2d5e7a7 100644
--- a/tests/bitmap:ip.t.list3
+++ b/tests/bitmap:ip.t.list3
@@ -1,8 +1,7 @@
Name: test
Type: bitmap:ip
Header: range 0.0.0.0-255.255.255.255 netmask 16 timeout 5
-Elements: 2
-Size in memory: 524288
+Size in memory: 524408
References: 0
Members:
0.0.0.0 timeout 0
diff --git a/tests/bitmap:ip.t.list4 b/tests/bitmap:ip.t.list4
new file mode 100644
index 0000000..53004cf
--- /dev/null
+++ b/tests/bitmap:ip.t.list4
@@ -0,0 +1,13 @@
+Name: test
+Type: bitmap:ip
+Header: range 2.0.0.1-2.1.0.0 timeout x
+Size in memory: 524408
+References: 0
+Members:
+2.0.0.1 timeout x
+2.0.0.128 timeout x
+2.0.0.129 timeout x
+2.0.0.130 timeout x
+2.0.0.131 timeout x
+2.1.0.0 timeout x
+
diff --git a/tests/bitmap:ip.t.list5 b/tests/bitmap:ip.t.list5
new file mode 100644
index 0000000..65d338d
--- /dev/null
+++ b/tests/bitmap:ip.t.list5
@@ -0,0 +1,9 @@
+Name: test
+Type: bitmap:ip
+Header: range 2.0.0.0-2.0.255.255 timeout x
+Size in memory: 524408
+References: 0
+Members:
+2.0.0.0 timeout x
+2.0.255.255 timeout x
+
diff --git a/tests/bitmap:ip.t.list6 b/tests/bitmap:ip.t.list6
new file mode 100644
index 0000000..13b75ad
--- /dev/null
+++ b/tests/bitmap:ip.t.list6
@@ -0,0 +1,265 @@
+Name: test
+Type: bitmap:ip
+Header: range 10.0.0.0-10.255.255.255 netmask 24 timeout x
+Size in memory: 524408
+References: 0
+Members:
+10.0.0.0 timeout x
+10.8.0.0 timeout x
+10.8.1.0 timeout x
+10.8.2.0 timeout x
+10.8.3.0 timeout x
+10.8.4.0 timeout x
+10.8.5.0 timeout x
+10.8.6.0 timeout x
+10.8.7.0 timeout x
+10.8.8.0 timeout x
+10.8.9.0 timeout x
+10.8.10.0 timeout x
+10.8.11.0 timeout x
+10.8.12.0 timeout x
+10.8.13.0 timeout x
+10.8.14.0 timeout x
+10.8.15.0 timeout x
+10.8.16.0 timeout x
+10.8.17.0 timeout x
+10.8.18.0 timeout x
+10.8.19.0 timeout x
+10.8.20.0 timeout x
+10.8.21.0 timeout x
+10.8.22.0 timeout x
+10.8.23.0 timeout x
+10.8.24.0 timeout x
+10.8.25.0 timeout x
+10.8.26.0 timeout x
+10.8.27.0 timeout x
+10.8.28.0 timeout x
+10.8.29.0 timeout x
+10.8.30.0 timeout x
+10.8.31.0 timeout x
+10.8.32.0 timeout x
+10.8.33.0 timeout x
+10.8.34.0 timeout x
+10.8.35.0 timeout x
+10.8.36.0 timeout x
+10.8.37.0 timeout x
+10.8.38.0 timeout x
+10.8.39.0 timeout x
+10.8.40.0 timeout x
+10.8.41.0 timeout x
+10.8.42.0 timeout x
+10.8.43.0 timeout x
+10.8.44.0 timeout x
+10.8.45.0 timeout x
+10.8.46.0 timeout x
+10.8.47.0 timeout x
+10.8.48.0 timeout x
+10.8.49.0 timeout x
+10.8.50.0 timeout x
+10.8.51.0 timeout x
+10.8.52.0 timeout x
+10.8.53.0 timeout x
+10.8.54.0 timeout x
+10.8.55.0 timeout x
+10.8.56.0 timeout x
+10.8.57.0 timeout x
+10.8.58.0 timeout x
+10.8.59.0 timeout x
+10.8.60.0 timeout x
+10.8.61.0 timeout x
+10.8.62.0 timeout x
+10.8.63.0 timeout x
+10.8.64.0 timeout x
+10.8.65.0 timeout x
+10.8.66.0 timeout x
+10.8.67.0 timeout x
+10.8.68.0 timeout x
+10.8.69.0 timeout x
+10.8.70.0 timeout x
+10.8.71.0 timeout x
+10.8.72.0 timeout x
+10.8.73.0 timeout x
+10.8.74.0 timeout x
+10.8.75.0 timeout x
+10.8.76.0 timeout x
+10.8.77.0 timeout x
+10.8.78.0 timeout x
+10.8.79.0 timeout x
+10.8.80.0 timeout x
+10.8.81.0 timeout x
+10.8.82.0 timeout x
+10.8.83.0 timeout x
+10.8.84.0 timeout x
+10.8.85.0 timeout x
+10.8.86.0 timeout x
+10.8.87.0 timeout x
+10.8.88.0 timeout x
+10.8.89.0 timeout x
+10.8.90.0 timeout x
+10.8.91.0 timeout x
+10.8.92.0 timeout x
+10.8.93.0 timeout x
+10.8.94.0 timeout x
+10.8.95.0 timeout x
+10.8.96.0 timeout x
+10.8.97.0 timeout x
+10.8.98.0 timeout x
+10.8.99.0 timeout x
+10.8.100.0 timeout x
+10.8.101.0 timeout x
+10.8.102.0 timeout x
+10.8.103.0 timeout x
+10.8.104.0 timeout x
+10.8.105.0 timeout x
+10.8.106.0 timeout x
+10.8.107.0 timeout x
+10.8.108.0 timeout x
+10.8.109.0 timeout x
+10.8.110.0 timeout x
+10.8.111.0 timeout x
+10.8.112.0 timeout x
+10.8.113.0 timeout x
+10.8.114.0 timeout x
+10.8.115.0 timeout x
+10.8.116.0 timeout x
+10.8.117.0 timeout x
+10.8.118.0 timeout x
+10.8.119.0 timeout x
+10.8.120.0 timeout x
+10.8.121.0 timeout x
+10.8.122.0 timeout x
+10.8.123.0 timeout x
+10.8.124.0 timeout x
+10.8.125.0 timeout x
+10.8.126.0 timeout x
+10.8.127.0 timeout x
+10.8.128.0 timeout x
+10.8.129.0 timeout x
+10.8.130.0 timeout x
+10.8.131.0 timeout x
+10.8.132.0 timeout x
+10.8.133.0 timeout x
+10.8.134.0 timeout x
+10.8.135.0 timeout x
+10.8.136.0 timeout x
+10.8.137.0 timeout x
+10.8.138.0 timeout x
+10.8.139.0 timeout x
+10.8.140.0 timeout x
+10.8.141.0 timeout x
+10.8.142.0 timeout x
+10.8.143.0 timeout x
+10.8.144.0 timeout x
+10.8.145.0 timeout x
+10.8.146.0 timeout x
+10.8.147.0 timeout x
+10.8.148.0 timeout x
+10.8.149.0 timeout x
+10.8.150.0 timeout x
+10.8.151.0 timeout x
+10.8.152.0 timeout x
+10.8.153.0 timeout x
+10.8.154.0 timeout x
+10.8.155.0 timeout x
+10.8.156.0 timeout x
+10.8.157.0 timeout x
+10.8.158.0 timeout x
+10.8.159.0 timeout x
+10.8.160.0 timeout x
+10.8.161.0 timeout x
+10.8.162.0 timeout x
+10.8.163.0 timeout x
+10.8.164.0 timeout x
+10.8.165.0 timeout x
+10.8.166.0 timeout x
+10.8.167.0 timeout x
+10.8.168.0 timeout x
+10.8.169.0 timeout x
+10.8.170.0 timeout x
+10.8.171.0 timeout x
+10.8.172.0 timeout x
+10.8.173.0 timeout x
+10.8.174.0 timeout x
+10.8.175.0 timeout x
+10.8.176.0 timeout x
+10.8.177.0 timeout x
+10.8.178.0 timeout x
+10.8.179.0 timeout x
+10.8.180.0 timeout x
+10.8.181.0 timeout x
+10.8.182.0 timeout x
+10.8.183.0 timeout x
+10.8.184.0 timeout x
+10.8.185.0 timeout x
+10.8.186.0 timeout x
+10.8.187.0 timeout x
+10.8.188.0 timeout x
+10.8.189.0 timeout x
+10.8.190.0 timeout x
+10.8.191.0 timeout x
+10.8.192.0 timeout x
+10.8.193.0 timeout x
+10.8.194.0 timeout x
+10.8.195.0 timeout x
+10.8.196.0 timeout x
+10.8.197.0 timeout x
+10.8.198.0 timeout x
+10.8.199.0 timeout x
+10.8.200.0 timeout x
+10.8.201.0 timeout x
+10.8.202.0 timeout x
+10.8.203.0 timeout x
+10.8.204.0 timeout x
+10.8.205.0 timeout x
+10.8.206.0 timeout x
+10.8.207.0 timeout x
+10.8.208.0 timeout x
+10.8.209.0 timeout x
+10.8.210.0 timeout x
+10.8.211.0 timeout x
+10.8.212.0 timeout x
+10.8.213.0 timeout x
+10.8.214.0 timeout x
+10.8.215.0 timeout x
+10.8.216.0 timeout x
+10.8.217.0 timeout x
+10.8.218.0 timeout x
+10.8.219.0 timeout x
+10.8.220.0 timeout x
+10.8.221.0 timeout x
+10.8.222.0 timeout x
+10.8.223.0 timeout x
+10.8.224.0 timeout x
+10.8.225.0 timeout x
+10.8.226.0 timeout x
+10.8.227.0 timeout x
+10.8.228.0 timeout x
+10.8.229.0 timeout x
+10.8.230.0 timeout x
+10.8.231.0 timeout x
+10.8.232.0 timeout x
+10.8.233.0 timeout x
+10.8.234.0 timeout x
+10.8.235.0 timeout x
+10.8.236.0 timeout x
+10.8.237.0 timeout x
+10.8.238.0 timeout x
+10.8.239.0 timeout x
+10.8.240.0 timeout x
+10.8.241.0 timeout x
+10.8.242.0 timeout x
+10.8.243.0 timeout x
+10.8.244.0 timeout x
+10.8.245.0 timeout x
+10.8.246.0 timeout x
+10.8.247.0 timeout x
+10.8.248.0 timeout x
+10.8.249.0 timeout x
+10.8.250.0 timeout x
+10.8.251.0 timeout x
+10.8.252.0 timeout x
+10.8.253.0 timeout x
+10.8.254.0 timeout x
+10.8.255.0 timeout x
+10.255.255.0 timeout x
+
diff --git a/tests/hash:ip,port,ip.t b/tests/hash:ip,port,ip.t
new file mode 100644
index 0000000..3c74cd9
--- /dev/null
+++ b/tests/hash:ip,port,ip.t
@@ -0,0 +1,51 @@
+# Range: Create a set with timeout
+0 ipset create test hash:ip,port,ip timeout 5
+# Range: Add partly zero valued element
+0 ipset add test 2.0.0.1,0,0.0.0.0
+# Range: Test partly zero valued element
+0 ipset test test 2.0.0.1,0,0.0.0.0
+# Range: Delete party zero valued element
+0 ipset del test 2.0.0.1,0,0.0.0.0
+# Range: Add almost zero valued element
+0 ipset add test 2.0.0.1,0,0.0.0.1
+# Range: Test almost zero valued element
+0 ipset test test 2.0.0.1,0,0.0.0.1
+# Range: Delete almost zero valued element
+0 ipset del test 2.0.0.1,0,0.0.0.1
+# Range: Add lower boundary
+0 ipset add test 2.0.0.1,5,1.1.1.1
+# Range: Add upper boundary
+0 ipset add test 2.1.0.0,128,2.2.2.2
+# Range: Test lower boundary
+0 ipset test test 2.0.0.1,5,1.1.1.1
+# Range: Test upper boundary
+0 ipset test test 2.1.0.0,128,2.2.2.2
+# Range: Test value not added to the set
+1 ipset test test 2.0.0.1,5,1.1.1.2
+# Range: Test value not added to the set
+1 ipset test test 2.0.0.1,6,1.1.1.1
+# Range: Test value not added to the set
+1 ipset test test 2.0.0.2,6,1.1.1.1
+# Range: Test value before lower boundary
+1 ipset test test 2.0.0.0,5,1.1.1.1
+# Range: Test value after upper boundary
+1 ipset test test 2.1.0.1,128,2.2.2.2
+# Range: Try to add value before lower boundary
+0 ipset add test 2.0.0.0,5,1.1.1.1
+# Range: Try to add value after upper boundary
+0 ipset add test 2.1.0.1,128,2.2.2.2
+# Range: List set
+0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
+# Range: Check listing
+0 diff -I 'Size in memory.*' .foo hash:ip,port,ip.t.list0 && rm .foo
+# Range: Sleep 6s so that elements can time out
+0 sleep 6
+# Range: List set
+0 ipset list test > .foo0 && ./sort.sh .foo0
+# Range: Check listing
+0 diff -I 'Size in memory.*' .foo hash:ip,port,ip.t.list1 && rm .foo
+# Range: Flush test set
+0 ipset flush test
+# Range: Delete test set
+0 ipset destroy test
+# eof
diff --git a/tests/hash:ip,port,ip.t.list0 b/tests/hash:ip,port,ip.t.list0
new file mode 100644
index 0000000..b20c8d8
--- /dev/null
+++ b/tests/hash:ip,port,ip.t.list0
@@ -0,0 +1,11 @@
+Name: test
+Type: hash:ip,port,ip
+Header: family inet hashsize 1024 maxelem 65536 timeout x
+Size in memory: 8720
+References: 0
+Members:
+2.0.0.0,5,1.1.1.1 timeout x
+2.0.0.1,5,1.1.1.1 timeout x
+2.1.0.0,128,2.2.2.2 timeout x
+2.1.0.1,128,2.2.2.2 timeout x
+
diff --git a/tests/hash:ip,port,ip.t.list1 b/tests/hash:ip,port,ip.t.list1
new file mode 100644
index 0000000..fb0b4a4
--- /dev/null
+++ b/tests/hash:ip,port,ip.t.list1
@@ -0,0 +1,7 @@
+Name: test
+Type: hash:ip,port,ip
+Header: family inet hashsize 1024 maxelem 65536 timeout 5
+Size in memory: 8304
+References: 0
+Members:
+
diff --git a/tests/hash:ip,port.t b/tests/hash:ip,port.t
new file mode 100644
index 0000000..cb88f96
--- /dev/null
+++ b/tests/hash:ip,port.t
@@ -0,0 +1,43 @@
+# Range: Create a set with timeout
+0 ipset create test hash:ip,port timeout 5
+# Range: Add partly zero valued element
+0 ipset add test 2.0.0.1,0
+# Range: Test partly zero valued element
+0 ipset test test 2.0.0.1,0
+# Range: Delete partly zero valued element
+0 ipset del test 2.0.0.1,0
+# Range: Add lower boundary
+0 ipset add test 2.0.0.1,5
+# Range: Add upper boundary
+0 ipset add test 2.1.0.0,128
+# Range: Test lower boundary
+0 ipset test test 2.0.0.1,5
+# Range: Test upper boundary
+0 ipset test test 2.1.0.0,128
+# Range: Test value not added to the set
+1 ipset test test 2.0.0.1,4
+# Range: Delete value not added to the set
+1 ipset del test 2.0.0.1,6
+# Range: Test value before lower boundary
+1 ipset test test 2.0.0.0,5
+# Range: Test value after upper boundary
+1 ipset test test 2.1.0.1,128
+# Range: Try to add value before lower boundary
+0 ipset add test 2.0.0.0,5
+# Range: Try to add value after upper boundary
+0 ipset add test 2.1.0.1,128
+# Range: List set
+0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
+# Range: Check listing
+0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list0 && rm .foo
+# Range: Sleep 6s so that elements can time out
+0 sleep 6
+# Range: List set
+0 ipset list test > .foo0 && ./sort.sh .foo0
+# Range: Check listing
+0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list1 && rm .foo
+# Range: Flush test set
+0 ipset flush test
+# Range: Delete test set
+0 ipset destroy test
+# eof
diff --git a/tests/hash:ip,port.t.list0 b/tests/hash:ip,port.t.list0
new file mode 100644
index 0000000..25d8632
--- /dev/null
+++ b/tests/hash:ip,port.t.list0
@@ -0,0 +1,11 @@
+Name: test
+Type: hash:ip,port
+Header: family inet hashsize 1024 maxelem 65536 timeout x
+Size in memory: 8592
+References: 0
+Members:
+2.0.0.0,5 timeout x
+2.0.0.1,5 timeout x
+2.1.0.0,128 timeout x
+2.1.0.1,128 timeout x
+
diff --git a/tests/hash:ip,port.t.list1 b/tests/hash:ip,port.t.list1
new file mode 100644
index 0000000..d455117
--- /dev/null
+++ b/tests/hash:ip,port.t.list1
@@ -0,0 +1,7 @@
+Name: test
+Type: hash:ip,port
+Header: family inet hashsize 1024 maxelem 65536 timeout 5
+Size in memory: 8304
+References: 0
+Members:
+
diff --git a/tests/hash:ip.t b/tests/hash:ip.t
index de6b0df..c7bddb8 100644
--- a/tests/hash:ip.t
+++ b/tests/hash:ip.t
@@ -18,12 +18,16 @@
0 ipset -A test 200.100.0.12
# IP: Delete the same value
0 ipset -D test 200.100.0.12
+# IP: List set
+0 ipset -L test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
+# IP: Check listing
+0 diff -I 'Size in memory.*' .foo hash:ip.t.list2 && rm .foo
# Sleep 6s so that element can time out
0 sleep 6
# IP: List set
0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0
# IP: Check listing
-0 diff .foo hash:ip.t.list0 && rm .foo
+0 diff -I 'Size in memory.*' .foo hash:ip.t.list0 && rm .foo
# IP: Flush test set
0 ipset -F test
# IP: Delete test set
@@ -58,20 +62,22 @@
0 ipset -T test 192.168.68.95
# Network: Test value not added to the set
1 ipset -T test 2.0.1.0
-# Network: List set
-0 ipset -L test > .foo && grep '2.0.0.0 timeout' .foo >/dev/null && grep '192.168.68.0 timeout' .foo >/dev/null && rm .foo
# Network: Add third element
0 ipset -A test 200.100.10.1 timeout 0
# Network: Add third random network
0 ipset -A test 200.100.0.12
# Network: Delete the same network
0 ipset -D test 200.100.0.12
+# Network: List set
+0 ipset -L test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
+# Network: Check listing
+0 diff -I 'Size in memory.*' -I 'Size in memory.*' .foo hash:ip.t.list3 && rm .foo
# Sleep 6s so that elements can time out
0 sleep 6
# Network: List set
0 ipset -L test > .foo
# Network: Check listing
-0 diff .foo hash:ip.t.list1 && rm .foo
+0 diff -I 'Size in memory.*' .foo hash:ip.t.list1 && rm .foo
# Network: Flush test set
0 ipset -F test
# Network: Delete test set
diff --git a/tests/hash:ip.t.list0 b/tests/hash:ip.t.list0
index cf2ecac..117faf2 100644
--- a/tests/hash:ip.t.list0
+++ b/tests/hash:ip.t.list0
@@ -1,8 +1,7 @@
Name: test
Type: hash:ip
-Header: hashsize 128 maxelem 65536 probes 4 resize 50 timeout 5
-Elements: 1
-Size in memory: 2048
+Header: family inet hashsize 128 maxelem 65536 timeout 5
+Size in memory: 1208
References: 0
Members:
192.168.68.69 timeout 0
diff --git a/tests/hash:ip.t.list1 b/tests/hash:ip.t.list1
index c564ba0..3189dae 100644
--- a/tests/hash:ip.t.list1
+++ b/tests/hash:ip.t.list1
@@ -1,8 +1,7 @@
Name: test
Type: hash:ip
-Header: hashsize 128 maxelem 65536 probes 4 resize 50 netmask 24 timeout 6
-Elements: 1
-Size in memory: 2048
+Header: family inet hashsize 128 maxelem 65536 netmask 24 timeout 6
+Size in memory: 1352
References: 0
Members:
200.100.10.0 timeout 0
diff --git a/tests/hash:ip.t.list2 b/tests/hash:ip.t.list2
new file mode 100644
index 0000000..468ba67
--- /dev/null
+++ b/tests/hash:ip.t.list2
@@ -0,0 +1,9 @@
+Name: test
+Type: hash:ip
+Header: family inet hashsize 128 maxe